其他分享
首页 > 其他分享> > cf自训6

cf自训6

作者:互联网

cf946D 背包+区间dp 好题

/*
先预处理出每行消去i个1后可以的到的最小时间:
    先求每行的前缀和,枚举左端点和右端点,消去的1 cost=tot-sum[r]+sum[l-1],区间长度=r-l+1
    time[cost]=min(time[cost],len) 
然后再进行行间处理:
    就是n行删k个,转化为n组物品,然后总代价是k 
    分组背包做一下完事 
dp[i][j]表示到第i行删了j个的最少上课时间 
*/ 
#include<bits/stdc++.h>
using namespace std;
#define maxn 505
int n,m,K,mp[maxn][maxn];
int t[maxn][maxn],sum[maxn][maxn],dp[maxn][maxn];

void init(){
    memset(t,0x3f,sizeof t);
    for(int i=1;i<=n;i++){
        t[i][sum[i][m]]=0;//全删完的情况 
        for(int l=1;l<=m;l++)
            for(int r=l;r<=m;r++){
                int len=r-l+1;
                int cost=sum[i][m]-(sum[i][r]-sum[i][l-1]);
                t[i][cost]=min(t[i][cost],r-l+1);
            }
        }
/*for(int i=1;i<=n;i++){
    for(int j=0;j<=sum[i][m];j++)
        cout<<t[i][j]<<" ";
    puts("");
}*/
}
int main(){
    cin>>n>>m>>K;
    char ch;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            cin>>ch;
            mp[i][j]=ch-'0';
            sum[i][j]=sum[i][j-1]+mp[i][j];
        }
    init();
    
    memset(dp,0x3f,sizeof dp);
    for(int j=0;j<=K;j++)dp[1][j]=t[1][j];
    for(int i=2;i<=n;i++)
        for(int j=0;j<=K;j++)
            for(int c=0;c<=j;c++)
                dp[i][j]=min(dp[i][j],dp[i-1][j-c]+t[i][c]);
    
    int ans=0x3f3f3f3f;
    for(int j=0;j<=K;j++)
        ans=min(ans,dp[n][j]);
    cout<<ans<<endl;
} 
View Code

cf893d 线段树区间更新,可以傻逼贪心做

/*
在每个0之前都要变成非负,这个非负数要尽量大。。
先求前缀和数组,从左往右扫,找到第一个在0前的负值点,将其提高到允许的最大值x,然后后面统一+x
线段树维护区间最大前缀和,然后从左往右扫,扫到ai=0时进行询问
如果该点前缀和小于0那么将其提高,提高的上限lim=d-后面区间最大前缀和,如果lim+当前值<0,那么不可行 
反之不进行操作
对前缀和先判一下。。 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define ll long long  
ll ans,n,d,a[maxn],sum[maxn],flag;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
ll Max[maxn<<2],lazy[maxn<<2];
void pushup(int rt){Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);}
void pushdown(int rt){
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];lazy[rt<<1|1]+=lazy[rt];
        Max[rt<<1]+=lazy[rt];Max[rt<<1|1]+=lazy[rt];
        lazy[rt]=0; 
    }
}
void build(int l,int r,int rt){
    if(l==r){Max[rt]=sum[l];return;}
    int m=l+r>>1;
    build(lson);build(rson);
    pushup(rt);
} 
void update(int L,int R,ll val,int l,int r,int rt){
    if(L<=l &&R>=r){lazy[rt]+=val;Max[rt]+=val;return;}
    int m=l+r>>1;
    pushdown(rt);
    if(L<=m)update(L,R,val,lson);
    if(R>m)update(L,R,val,rson);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r)return Max[rt];
    pushdown(rt);
    int m=l+r>>1;ll res=-0x3f3f3f3f;
    if(L<=m)res=query(L,R,lson);
    if(R>m)res=max(res,query(L,R,rson));
    return res;
}
int main(){
    cin>>n>>d;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++){sum[i]=sum[i-1]+a[i];if(sum[i]>d)flag=1;}
    if(flag){
        puts("-1");
        return 0;
    }
    build(1,n,1);
    for(int i=1;i<=n;i++){
        if(a[i]==0){
            int tmp=query(i,i,1,n,1);
            if(tmp>=0)continue;
            else{
                int Max=query(i,n,1,n,1);
                int lim=d-Max;
                if(tmp+lim<0 || flag){
                    puts("-1");
                    return 0;
                }
                else {
                    update(i,n,lim,1,n,1);
                    ans++;
                }
            }
        }
    }
    cout<<ans<<endl;
}
View Code

cf797E 复杂度均摊成根号n级别,新题

#include<bits/stdc++.h>
using namespace std;
#define maxn 120005
int q,n,a[maxn];
int dp[maxn][340];//dp[i][j]表示在位置i(p=i),k=j时的跳跃步数,dp[i][j]=dp[i+a[i]+j][j]+1 
void init(){
    for(int j=1;j<=330;j++)
        for(int i=n;i>=1;i--){
            if(i+a[i]+j>n)dp[i][j]=1;
            else dp[i][j]=dp[i+a[i]+j][j]+1;
        }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    init();
        
    cin>>q;
    while(q--){
        int ans=0,p,k;
        cin>>p>>k;
        if(k<=330){
            cout<<dp[p][k]<<'\n';
            continue;
        }
        while(p<=n){
            p+=a[p]+k;
            ans++;
        }
        cout<<ans<<'\n';
    }
}
View Code

cf46D 线段树区间合并沙比提。。一个vector下标搞错弄了好久

#include<bits/stdc++.h>
using namespace std;
#define maxn 120005
int q,n,a[maxn];
int dp[maxn][340];//dp[i][j]表示在位置i(p=i),k=j时的跳跃步数,dp[i][j]=dp[i+a[i]+j][j]+1 
void init(){
    for(int j=1;j<=330;j++)
        for(int i=n;i>=1;i--){
            if(i+a[i]+j>n)dp[i][j]=1;
            else dp[i][j]=dp[i+a[i]+j][j]+1;
        }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    init();
        
    cin>>q;
    while(q--){
        int ans=0,p,k;
        cin>>p>>k;
        if(k<=330){
            cout<<dp[p][k]<<'\n';
            continue;
        }
        while(p<=n){
            p+=a[p]+k;
            ans++;
        }
        cout<<ans<<'\n';
    }
}
View Code

 

标签:rt,int,cf,cin,自训,init,maxn,dp
来源: https://www.cnblogs.com/zsben991126/p/10648277.html