【考试总结】2022-04-03

Max

fi,S,t 表示 S 集合中的操作都选中了 i 且使其值变成了 t 的概率,平凡状压 DP 即可

再计算 gi,S,t 表示 S 集合选中了前 i 个元素且最大值是 t 的概率,枚举子集之后前缀和优化即可

当然可以子集卷积但是没必要

时间复杂度 Θ(nmc3m)

Code Display
int f[42][1024][35];
int dp[42][1024][35];
int n,m,c;
int p[50][20][10];
int sum1[100],sum2[100];
signed main(){
    freopen("max.in","r",stdin); freopen("max.out","w",stdout);
    n=read(); m=read(); c=read();
    for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) for(int k=0;k<=c;++k) p[j][i][k]=read();
    int S=1<<m;
    for(int i=1;i<=n;++i){
        f[i][0][0]=1;
        for(int j=1;j<S;++j){
            int lst=j-(j&(-j)),x=1+__builtin_ctzll(j&(-j));
            for(int k=0;k<=m*c;++k) if(f[i][lst][k]){
                rep(d,0,c) ckadd(f[i][j][k+d],mul(f[i][lst][k],p[i][x][d]));
            }
        }
    }
    dp[0][0][0]=1;
    for(int i=1;i<=n;++i){
        for(int s1=0;s1<S;++s1){
            int T=(S-1)^s1;
            for(int s2=T;s2;s2=(s2-1)&T){
                sum1[0]=dp[i-1][s1][0];
                sum2[0]=f[i][s2][0];
                rep(h,1,m*c){
                    sum1[h]=add(sum1[h-1],dp[i-1][s1][h]);
                    sum2[h]=add(sum2[h-1],f[i][s2][h]);
                }
                ckadd(dp[i][s1^s2][0],mul(f[i][s2][0],dp[i-1][s1][0]));
                rep(h,1,m*c){
                    dp[i][s1^s2][h]+=f[i][s2][h]*dp[i-1][s1][h]+sum1[h-1]*f[i][s2][h]+sum2[h-1]*dp[i-1][s1][h];
                    dp[i][s1^s2][h]%=mod;
                }
            }
        }
        for(int j=0;j<S;++j) for(int h=0;h<=m*c;++h) ckadd(dp[i][j][h],dp[i-1][j][h]);
    }
    int ans=0;
    for(int i=1;i<=m*c;++i) ckadd(ans,mul(i,dp[n][S-1][i]));
    print(ans);
    return 0;
}

Paint

答案下界是 2(max(w,h)+1) 所以矩形一定跨过 x=w2 或者 y=h2 否则蜷缩在角上一定不优

以跨过 y=w2 为例,枚举矩形的右边界并维护所有可能左边界的答案,这时对于每个左边的点其另一维边界就是它到当前枚举的边界中间区域 y 值大于其第一个点以及小于等于其第一个点对应的区间

可以使用单调栈配合线段树区间加法维护出最大合法区间长度,跨过另一维的处理方法是将两维坐标交换

Code Display
const int N=2e5+10;
int w,h,n;
pair<int,int> a[N];
int tag[N<<2],mx[N<<2];
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
inline void push_tag(int p,int v){tag[p]+=v; mx[p]+=v;}
inline void push_down(int p){
    if(tag[p]){
        push_tag(ls,tag[p]);
        push_tag(rs,tag[p]);
        tag[p]=0;
    } return ;
}
inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){
    if(st<=l&&r<=ed) return push_tag(p,v);
    int mid=(l+r)>>1; push_down(p);
    if(st<=mid) upd(st,ed,v,lson);
    if(ed>mid) upd(st,ed,v,rson);
    mx[p]=max(mx[ls],mx[rs]);
    return ;
}
#undef ls
#undef rs
#undef lson
#undef rson
int ans=0;
inline void solve(){
    memset(tag,0,sizeof(tag));
    memset(mx,0,sizeof(mx));
    sort(a+1,a+n+1);
    static pair<int,int> stk1[N],stk2[N];
    int top1=0,top2=0;
    for(int i=1;i<=n;++i){
        if(a[i].sec<=h/2){
            int lst=i-1;
            while(top1&&stk1[top1].sec<a[i].sec){
                upd(stk1[top1].fir,lst,stk1[top1].sec-a[i].sec);
                lst=stk1[top1--].fir-1;
            }
            if(lst!=i-1) stk1[++top1]={lst+1,a[i].sec};
        }else{
            int lst=i-1;
            while(top2&&stk2[top2].sec>a[i].sec){
                upd(stk2[top2].fir,lst,a[i].sec-stk2[top2].sec);
                lst=stk2[top2--].fir-1;
            }
            if(lst!=i-1) stk2[++top2]={lst+1,a[i].sec};
        }
        stk1[++top1]={i,0};
        stk2[++top2]={i,h};
        upd(i,i,h-a[i].fir);
        ckmax(ans,a[i+1].fir+mx[1]);   
    }
    return ;
}
signed main(){
    freopen("paint.in","r",stdin); freopen("paint.out","w",stdout);
    w=read(); h=read();
    n=read();
    rep(i,1,n) a[i].fir=read(),a[i].sec=read();
    a[++n]={0,0}; a[++n]={w,h};
    solve();
    for(int i=1;i<=n;++i) swap(a[i].fir,a[i].sec);
    swap(w,h);
    solve();
    print(ans*2);
    return 0;
}

Decompose

fi,j 表示 i 所在的重链长度已经为 ji 的子树内最大权值和,转移枚举找到一个儿子使其长度为 j1 剩下的选 max_element 即可

那么变成了基础的 DDP 练习题,线段树 (max,+) 矩阵再用 std::set 维护轻儿子信息即可

Code Display
const int N=1e5+10,inf=1e18;
int n,Q,L,w[N][5],dp[N][5];
vector<int> G[N];
struct matrix{
    int a[5][5]; matrix(){memset(a,-0x3f,sizeof(a));}
    matrix operator *(const matrix &b) const{
        matrix res;
        rep(i,1,L) rep(k,1,L) rep(j,1,L) ckmax(res.a[i][j],a[i][k]+b.a[k][j]);
        return res;
    }
}t[N<<2],Bas;
int top[N],dfn[N],siz[N],ord[N],son[N],fa[N],dep[N],tim;
multiset<int> maxc[N][5];
int sumc[N],bot[N];
inline int Val(int v){return v<=-1e16?-inf:v;}
inline void dfs1(int x,int fat){
    dep[x]=dep[fa[x]=fat]+(siz[x]=1);
    for(auto t:G[x]){
        dfs1(t,x); siz[x]+=siz[t];
        if(siz[t]>siz[son[x]]) son[x]=t;
    }
    int sumf=0;
    for(auto t:G[x]) sumf+=*max_element(dp[t]+1,dp[t]+L+1);
    for(int i=1;i<=L;++i){
        int Mx=-inf;
        for(auto t:G[x]) ckmax(Mx,dp[t][i-1]-(*max_element(dp[t]+1,dp[t]+L+1)));
        if(i==1) Mx=0;
        dp[x][i]=sumf+w[x][i]+Mx;
    }
    return ;
}
inline void dfs2(int x,int topf){
    top[x]=topf; ord[dfn[x]=++tim]=x;
    bot[topf]=x;
    if(son[x]) dfs2(son[x],topf); 
    for(auto t:G[x]) if(t!=son[x]){
        dfs2(t,t);
        rep(i,2,L) maxc[x][i].insert(Val(dp[t][i-1]-(*max_element(dp[t]+1,dp[t]+L+1))));
        sumc[x]+=*max_element(dp[t]+1,dp[t]+L+1);
    }
    return ;
}
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
inline int Maxc(int x,int c){
    if(!maxc[x][c].size()) return -inf;
    return *prev(maxc[x][c].end());
}
inline void get_matrix(int p,int x){
    for(int i=1;i<=L;++i){
        for(int j=1;j<=L;++j){
            t[p].a[i][j]=sumc[x]+w[x][j];
            if(j!=1&&i!=j-1) t[p].a[i][j]+=Maxc(x,j);
        }
    } return ;
}
inline void build(int p,int l,int r){
    if(l==r) return get_matrix(p,ord[l]);
    int mid=(l+r)>>1;
    build(p<<1,l,mid); build(p<<1|1,mid+1,r);
    t[p]=t[p<<1|1]*t[p<<1];
}
inline void upd(int pos,int p=1,int l=1,int r=n){
    if(l==r) return get_matrix(p,ord[l]);
    int mid=(l+r)>>1;
    if(pos<=mid) upd(pos,lson); else upd(pos,rson);
    t[p]=t[p<<1|1]*t[p<<1];
    return ;
}
inline matrix query(int st,int ed,int p=1,int l=1,int r=n){
    if(st<=l&&r<=ed) return t[p];
    int mid=(l+r)>>1;
    if(ed<=mid) return query(st,ed,lson);
    if(st>mid) return query(st,ed,rson);
    return query(st,ed,rson)*query(st,ed,lson);
}
inline int get_dp(int x,int c=-1){
    if(dep[bot[x]]-dep[x]+1<c) return -inf;
    matrix res=bot[x]==x?Bas:query(dfn[x],dfn[bot[x]]-1);
    if(~c) return w[bot[x]][1]+res.a[1][c];
    int Mx=-inf;
    for(int i=1;i<=L;++i) ckmax(Mx,res.a[1][i]);
    return w[bot[x]][1]+Mx;
}
#undef ls
#undef rs
#undef lson
#undef rson
// Maintain sumc,maxc
inline void erase(int x){
    while(top[x]!=1){
        x=top[x];
        matrix res=bot[x]==x?Bas:query(dfn[x],dfn[bot[x]]-1);
        int Mx=-inf;
        rep(i,1,L){
            int val=Val(w[bot[x]][1]+res.a[1][i]);
            ckmax(Mx,val);
        }
        rep(i,2,L){
            int val=Val(w[bot[x]][1]+res.a[1][i-1]-Mx);
            maxc[fa[x]][i].erase(maxc[fa[x]][i].find(val));
        }
        sumc[fa[x]]-=Mx;
        x=fa[x];
    } return ;
}
inline void insert(int x){
    upd(dfn[x]);
    while(top[x]!=1){
        x=top[x];
        matrix res=bot[x]==x?Bas:query(dfn[x],dfn[bot[x]]-1);
        int Mx=-inf;
        rep(i,1,L){
            int val=Val(w[bot[x]][1]+res.a[1][i]);
            ckmax(Mx,val);
        }
        rep(i,2,L){
            int val=Val(w[bot[x]][1]+res.a[1][i-1]-Mx);
            maxc[fa[x]][i].insert(val);
        }
        sumc[fa[x]]+=Mx;
        upd(dfn[fa[x]]);
        x=fa[x];
    } return ;
}
signed main(){
    freopen("decompose.in","r",stdin); freopen("decompose.out","w",stdout);
    n=read(); Q=read(); L=read();
    rep(i,2,n) G[fa[i]=read()].emplace_back(i); 
    for(int i=1;i<=n;++i){
        rep(j,1,L) w[i][j]=read();
    }
    Bas.a[1][1]=0;
    dfs1(1,0); dfs2(1,1); build(1,1,n);
    while(Q--){
        int x=read();
        erase(x);
        rep(i,1,L) w[x][i]=read();
        insert(x);
        print(get_dp(1));
    }
    return 0;
}

posted @   没学完四大礼包不改名  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示