[CTS2019]氪金手游(容斥+树形背包DP)
降智好题。本蒟蒻VP时没想到怎么做被题面迷惑了,只会20分的“好”成绩。简直自闭了。
首先显然度为0的点是白给的,根据等比数列求和公式即可求得。然后考虑这个树如果是一颗外向树,就是每个点先父亲再自己。然后直接DP,令f[i][j]表示子树i内Σw=j的概率,转移时直接用背包转移一发即可。边是正向的直接转移,反向的加上去掉该限制的答案,并减去反向的答案。复杂度显然是O(n2)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N=1019,mod=998244353; int n,cnt,ans,p[N][4],f[N][N*3],g[N*3],sz[N],inv[N*3],hd[N],v[N<<1],nxt[N<<1],w[N<<1]; void add(int&x,int y){x=x+y>=mod?x+y-mod:x+y;} int qpow(int a,int b) { int ret=1; while(b) { if(b&1)ret=1ll*ret*a%mod; a=1ll*a*a%mod,b>>=1; } return ret; } void adde(int x,int y) { v[++cnt]=y,w[cnt]=1,nxt[cnt]=hd[x],hd[x]=cnt; v[++cnt]=x,w[cnt]=0,nxt[cnt]=hd[y],hd[y]=cnt; } void dfs(int u,int fa) { f[u][0]=1; for(int i=hd[u];i;i=nxt[i]) if(v[i]!=fa) { dfs(v[i],u); for(int j=0;j<=sz[u]+sz[v[i]];j++)g[j]=0; for(int j=0;j<=sz[u];j++) for(int k=0;k<=sz[v[i]];k++) { int val=1ll*f[u][j]*f[v[i]][k]%mod; if(w[i])add(g[j+k],val);else add(g[j+k],mod-val),add(g[j],val); } sz[u]+=sz[v[i]]; for(int j=0;j<=sz[u];j++)f[u][j]=g[j]; } memset(g,0,sizeof g); for(int i=0;i<=sz[u];i++) for(int j=1;j<=3;j++) add(g[i+j],1ll*f[u][i]*p[u][j]%mod*j%mod*inv[i+j]%mod); sz[u]+=3; for(int i=0;i<=sz[u];i++)f[u][i]=g[i]; } int main() { scanf("%d",&n); for(int i=1,x,y,z,sum;i<=n;i++) { scanf("%d%d%d",&x,&y,&z); sum=qpow(x+y+z,mod-2); p[i][1]=1ll*x*sum%mod,p[i][2]=1ll*y*sum%mod,p[i][3]=1ll*z*sum%mod; } inv[0]=inv[1]=1;for(int i=2;i<=n*3;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),adde(x,y); dfs(1,0); for(int i=0;i<=3*n;i++)add(ans,f[1][i]); printf("%d",ans); }