洛谷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; }
码量接近猪国杀