洛谷P5281/LOJ3044/UOJ468/BZOJ5516[ZJOI2019]Minimax搜索(动态DP+树链剖分)

一个显而易见的事实是对于$w(S)=1$当且仅当$W\in S$,方案数为$2^{m-1}$,因此接下来就可以只讨论不修改$W$的$2^{m-1}-1$种情况。

那么我们只有两种选择:把$<W$的叶子改成$>W$,让根$>W$;或者把$>W$的叶子改成$<W$,让根$<W$,两种方案互不影响。一个贪心的选择是要改就改成$W+1$,$W-1$,这样$w(S)=k$就对应着修改$[W-k+1,W+k-1]$内非W的叶子的情况。

既然两种方案互不影响,我们不妨分开dp:记$f_u=\text{随机取合法叶子集合,使u的权值=W-1的概率}$,$g_u=\text{随机取合法叶子集合,使u的权值=W+1的概率}$,其中本来就满足的结点值为1;$\in [W-k+1,W+k-1]$的叶子值为$\frac{1}{2}$,否则为0,则$w(S)\le k$的$S$个数为$2^{m-1}+2^{m-1}\times (1-(1-f_1)(1-g_1))$。

再考虑转移:对于Max结点,$f_u=1-\prod\limits_v(1-f_v)$,$g_u=\prod\limits_vg_v$;对于Min结点$f_u=\prod\limits_vf_v$,$g_u=1-\prod\limits_v(1-g_v)$,这个转移非常难写,考虑转化:设$f_u'=[dep_u\;mod\;2=0](1-f_u)+[dep_u\;mod\;2=1]f_u$,则$f_u'=\prod\limits_v(1-f_v')$,对于$g$反一下,转移式也相同,然后顺序枚举k,每次修改$W-k+1$,$W+k-1$的叶子,用著名的DDP就可以$O(nlog^2n)$求出$f,g$,差分一下输出即可。

#include<cstdio>
typedef long long ll;
const int mod=998244353;
const int inv2=499122177;
const int N=200050;
char rB[1<<21],*rS,*rT,wB[(1<<21)+50];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[15];
inline void wt(int x){
    if(wp>(1<<21))flush();
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    wB[++wp]=x|48;
    while(l>=0)wB[++wp]=buf[l--]|48;
    wB[++wp]=' ';
}
int a[N],G[N],to[N<<1],nxt[N<<1],cnt=0,f[N],dep[N],sz[N],son[N],top[N],ed[N],id[N],dfsc=0,n,m=0,p1[N],p2[N],ans[N],dp[N];
bool islf[N];
inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}
inline int inc(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int fp(int a,int p){
    int s=1;
    while(p){
        if(p&1)s=(ll)s*a%mod;
        a=(ll)a*a%mod;
        p>>=1;
    }
    return s;
}
struct num{  //中间可能出现*0,/0的情况,所以要记下0的指数
    int v,p0;
    num(){}
    num(int x){
        if(x){v=x;p0=0;}
        else{v=1;p0=1;}
    }
    inline void operator *=(int x){
        if(x)v=(ll)v*x%mod;
        else ++p0;
    }
    inline void operator /=(int x){
        if(x)v=(ll)v*fp(x,mod-2)%mod;
        else --p0;
    }
    inline int toi(){return p0?0:v;}
}ret[N];  //记录轻儿子的乘积
struct node{  //因为是一维式子,y=kx+b比矩阵简单
    int k,b;
    node(){}
    node(int k,int b):k(k),b(b){}
    inline node operator +(const node &y)const{return node((ll)k*y.k%mod,((ll)k*y.b+b)%mod);}
}sum[N<<2];
inline void adde(int u,int v){
    to[++cnt]=v;nxt[cnt]=G[u];G[u]=cnt;
    to[++cnt]=u;nxt[cnt]=G[v];G[v]=cnt;
}
void dfs1(int u,int fa){
    int i,v,maxn=0;
    dep[u]=dep[f[u]=fa]+1;sz[u]=islf[u]=1;
    a[u]=(dep[u]&1)?0:n+1;
    for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa){
        islf[u]=0;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>maxn){son[u]=v;maxn=sz[v];}
        a[u]=(dep[u]&1)?Max(a[u],a[v]):Min(a[u],a[v]);
    }
    if(islf[u]){
        ++m;
        a[u]=u;
    }
}
void dfs2(int u,int topf){
    top[ed[topf]=u]=topf;
    id[u]=++dfsc;
    if(son[u]){
        dfs2(son[u],topf);
        for(int i=G[u],v;i;i=nxt[i])if((v=to[i])!=f[u]&&v!=son[u])dfs2(v,v);
    }
}
void dfs3(int u){
    if(islf[u]){
        ret[id[u]]=num(dp[u]=(dep[u]&1)?u<a[1]:1-(u<a[1]));
        return;
    }
    ret[id[u]]=num(1);
    for(int i=G[u],v;i;i=nxt[i])if((v=to[i])!=f[u]&&v!=son[u]){
        dfs3(v);
        ret[id[u]]*=dec(1,dp[v])%mod;
    }
    dfs3(son[u]);
    dp[u]=(ll)ret[id[u]].toi()*dec(1,dp[son[u]])%mod;
}
void dfs4(int u){
    if(islf[u]){
        ret[id[u]]=num(dp[u]=(dep[u]&1)?1-(u>a[1]):u>a[1]);
        return;
    }
    ret[id[u]]=num(1);
    for(int i=G[u],v;i;i=nxt[i])if((v=to[i])!=f[u]&&v!=son[u]){
        dfs4(v);
        ret[id[u]]*=dec(1,dp[v])%mod;
    }
    dfs4(son[u]);
    dp[u]=(ll)ret[id[u]].toi()*dec(1,dp[son[u]])%mod;
}
void build(int o,int L,int R){
    if(L==R)sum[o]=node(dec(0,ret[L].toi()),ret[L].toi());
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        build(lc,L,M);build(rc,M+1,R);
        sum[o]=sum[lc]+sum[rc];
    }
}
void change(int o,int L,int R,int x){
    if(L==R)sum[o]=node(dec(0,ret[x].toi()),ret[x].toi());
    else{
        int lc=o<<1,rc=lc|1,M=L+R>>1;
        if(x<=M)change(lc,L,M,x);
        else change(rc,M+1,R,x);
        sum[o]=sum[lc]+sum[rc];
    }
}
node ask(int o,int L,int R,int x,int y){
    if(x<=L&&y>=R)return sum[o];
    int lc=o<<1,rc=lc|1,M=L+R>>1;
    if(x<=M)if(y>M)return ask(lc,L,M,x,y)+ask(rc,M+1,R,x,y);
    else return ask(lc,L,M,x,y);
    else return ask(rc,M+1,R,x,y);
}
inline int query(int u){
    return ask(1,1,n,id[u],id[ed[u]]).b;
}
inline void update(int u){
    int topf,pre,cur;
    ret[id[u]]=num(inv2);
    while(u){
        topf=top[u];
        pre=query(topf);
        change(1,1,n,id[u]);
        cur=query(topf);
        if(u=f[topf]){
            ret[id[u]]/=dec(1,pre);ret[id[u]]*=dec(1,cur);
        }
    }
}
int main(){
    int L,R,i,u,v,tot;
    n=rd();L=rd();R=rd();
    for(i=1;i<n;++i){
        u=rd();v=rd();
        adde(u,v);
    }
    dfs1(1,0);
    dfs2(1,1);
    ans[1]=tot=fp(2,m-1);ans[n]=((tot<<1)-1)%mod;
    dfs3(1);
    build(1,1,n);
    for(i=2;i<n;++i){
        if(a[1]+i-1<=n&&islf[a[1]+i-1])update(a[1]+i-1);
        p1[i]=query(1);
    }
    dfs4(1);
    build(1,1,n);
    for(i=2;i<n;++i){
        if(a[1]-i+1>0&&islf[a[1]-i+1])update(a[1]-i+1);
        p2[i]=dec(1,query(1));
    }
    for(i=2;i<n;++i)ans[i]=(tot+(ll)tot*dec(1,(ll)dec(1,p1[i])*dec(1,p2[i])%mod))%mod;
    for(i=L;i<=R;++i)wt(dec(ans[i],ans[i-1]));
    flush();
    return 0;
}
View Code

码量接近猪国杀

猜你喜欢

转载自www.cnblogs.com/sunshine-chen/p/11400632.html