設修改前根節點的權值爲 \(W\),不難發現,必定存在一條從根到葉子的鏈,鏈上的每一個點權值都爲 \(W\),那麼只要這條鏈上任意一點權值改變,根節點權值最後必定會改變。c++
考慮一個葉子 \(i\),且 \(w_i \neq W\),要想經過改變該點權值來讓鏈上的點發生改變,該點必須知足如下一個狀況:git
\(w_i<W\),且該點祖先和鏈的第一個交點的深度爲奇數,這時將其改成 \(W+1\)。優化
\(w_i > W\),且該點祖先和鏈的第一個交點的深度爲偶數,這時將其改成 \(W-1\)。spa
直接處理代價爲 \(k\) 的狀況很差處理,考慮求出代價 \(\leqslant k\) 的方案後差分。code
將問題轉化爲機率來考慮,鏈上的點設 \(f_x\) 表示 \(x\) 子樹內的葉子經過代價 \(\leqslant k\) 的修改後 \(x\) 權值不變的機率,得最終方案數爲總方案乘 \(1-\prod f_x\)。考慮不在鏈上的點,若其深度爲奇數,\(DP\) 值爲權值 \(<W\) 的機率,若其深度爲偶數,\(DP\) 值爲權值 \(>W\) 的機率,得轉移爲:get
這樣作須要對每一個 \(k\) 都 \(DP\) 一遍,複雜度沒法接受。所以將 \(k\) 也加到狀態中,對全部 \(k\) 一塊兒 \(DP\),用線段樹合併實現總體 \(DP\) 便可。it
同時發如今枚舉 \(k\) 的過程當中,每一個點是否要修改的狀況只會改變一次,所以也能用動態 \(DP\) 來優化。class
暴力 \(DP\),有 \(70\) 分。di
#include<bits/stdc++.h> #define maxn 400010 #define inf 1000000000 #define p 998244353 using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,L,R,k,pw=1; int dep[maxn],val[maxn],ans[maxn]; ll f[maxn]; struct edge { int to,nxt; edge(int a=0,int b=0) { to=a,nxt=b; } }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to) { e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt; } void dfs_pre(int x,int fa) { val[x]=(dep[x]=dep[fa]+1)&1?-inf:inf; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dfs_pre(y,x),val[x]=dep[x]&1?max(val[x],val[y]):min(val[x],val[y]); } if(abs(val[x])==inf) val[x]=x,pw=pw*2%p; } void dp(int x,int fa,int d) { if(x==val[x]) { if(abs(x-val[1])<k&&(d&1?x<val[1]:x>val[1])) f[x]=(p+1)>>1; else f[x]=dep[x]&1?x<val[1]:x>val[1]; return; } f[x]=1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dp(y,x,d),f[x]=f[x]*(1-f[y]+p)%p; } } void dfs(int x,int fa) { f[x]=x==val[x]?(p+1)>>1:1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; if(val[x]==val[y]) dfs(y,x),f[1]=f[1]*f[y]%p; else dp(y,x,dep[x]),f[x]=f[x]*(1-f[y]+p)%p; } } int main() { read(n),read(L),read(R); for(int i=1;i<n;++i) { int x,y; read(x),read(y); add(x,y),add(y,x); } dfs_pre(1,0); for(int i=max(L-1,1);i<=R;++i) { k=i; if(i==n) ans[i]=(pw-1+p)%p; else dfs(1,0),ans[i]=(1-f[1]+p)*pw%p; if(i>=L) printf("%d ",(ans[i]-ans[i-1]+p)%p); } return 0; }
線段樹合併,總體 \(DP\)。ant
#include<bits/stdc++.h> #define maxn 400010 #define maxm 12000010 #define inf 1000000000 #define p 998244353 #define mid ((l+r)>>1) using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,L,R,k,pw=1,tot; int dep[maxn],val[maxn],ans[maxn],rt[maxn],ls[maxm],rs[maxm],add[maxm],mul[maxm]; struct edge { int to,nxt; edge(int a=0,int b=0) { to=a,nxt=b; } }e[maxn]; int head[maxn],edge_cnt; void link(int from,int to) { e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt; } void dfs_pre(int x,int fa) { val[x]=(dep[x]=dep[fa]+1)&1?-inf:inf; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dfs_pre(y,x),val[x]=dep[x]&1?max(val[x],val[y]):min(val[x],val[y]); } if(abs(val[x])==inf) val[x]=x,pw=pw*2%p; } int get() { mul[++tot]=1; return tot; } void pushadd(int &x,int v) { if(!x) x=get(); add[x]=(add[x]+v)%p; } void pushmul(int &x,int v) { if(!x) x=get(); add[x]=(ll)add[x]*v%p,mul[x]=(ll)mul[x]*v%p; } void pushdown(int x) { if(mul[x]!=1) pushmul(ls[x],mul[x]),pushmul(rs[x],mul[x]),mul[x]=1; if(add[x]) pushadd(ls[x],add[x]),pushadd(rs[x],add[x]),add[x]=0; } void modify(int L,int R,int l,int r,int v,int &cur) { if(!cur) cur=get(); if(L<=l&&R>=r) { pushadd(cur,v); return; } pushdown(cur); if(L<=mid) modify(L,R,l,mid,v,ls[cur]); if(R>mid) modify(L,R,mid+1,r,v,rs[cur]); } int merge(int x,int y) { if(!x&&!y) return 0; if(!ls[y]&&!rs[y]) { pushmul(x,add[y]); return x; } if(!ls[x]&&!rs[x]) { pushmul(y,add[x]); return y; } pushdown(x),pushdown(y); ls[x]=merge(ls[x],ls[y]),rs[x]=merge(rs[x],rs[y]); return x; } void solve(int l,int r,int cur) { if(!ls[cur]&&!rs[cur]) { for(int i=l;i<=r;++i) ans[i]=(ll)(1-add[cur]+p)*pw%p; return; } pushdown(cur),solve(l,mid,ls[cur]),solve(mid+1,r,rs[cur]); } void dp(int x,int fa,int d) { if(x==val[x]) { if(d&1?x<val[1]:x>val[1]) modify(1,abs(x-val[1]),1,n-1,dep[x]&1?x<val[1]:x>val[1],rt[x]),modify(abs(x-val[1])+1,n-1,1,n-1,(p+1)>>1,rt[x]); else pushadd(rt[x],dep[x]&1?x<val[1]:x>val[1]); return; } pushadd(rt[x],1); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dp(y,x,d),pushmul(rt[y],p-1),pushadd(rt[y],1),rt[x]=merge(rt[x],rt[y]); } } void dfs(int x,int fa) { pushadd(rt[x],x==val[x]?(p+1)>>1:1); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; if(val[x]==val[y]) dfs(y,x),rt[1]=merge(rt[1],rt[y]); else dp(y,x,dep[x]),pushmul(rt[y],p-1),pushadd(rt[y],1),rt[x]=merge(rt[x],rt[y]); } } int main() { read(n),read(L),read(R); for(int i=1;i<n;++i) { int x,y; read(x),read(y); link(x,y),link(y,x); } dfs_pre(1,0),ans[n]=(pw-1+p)%p,dfs(1,0),solve(1,n-1,rt[1]); for(int i=L;i<=R;++i) printf("%d ",(ans[i]-ans[i-1]+p)%p); return 0; }