[期望DP][树形DP]JZOJ 5233 概率博弈
分析
我们发现如果要直接按权值求方案未免太难,我们不妨设最终答案为x,则小于等于x的权值设为0,大于的设为1
设f[i][j][0/1]为在以i为根的子树中,有j个叶子节点为0,且i选择到的是0/1的期望
那么转移是比较显然的,可以用一个DFS枚举从子节点传上来的叶子数量再转移(O(n)复杂度)
然后对于层数我们显然小A或小B一定只会取1或0,那么另一个怎么求呢?我们只需要求出当前j与子树内叶子总数的组合数再-得到的情况即可
最后我们统计答案时还需要考虑排列,所以需要乘上i!*(sz-i)!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int N=5e3+10; const ll P=1e9+7; struct Edge { int u,v,nx; }g[2*N]; int cnt,list[N],sz[N],a[N]; ll f[N][N][2],fact[N],ans; int n,m,size; void Add(int u,int v) { g[++cnt]=(Edge){u,v,list[u]};list[u]=cnt; } ll Power(ll x,ll y) {ll ans=1ll;for (;y;y>>=1,(x*=x)%=P) if (y&1) (ans*=x)%=P;return ans;} ll C(ll n,ll m) {return fact[n]*Power(fact[m],P-2)%P*Power(fact[n-m],P-2)%P;} void DFS(int dep,int u,int now,int cont,ll e) { if (dep>size) {(f[u][cont][now]+=e)%=P;return;} for (int i=0;i<=sz[a[dep]];i++) if (f[a[dep]][i][now]) DFS(dep+1,u,now,cont+i,e*f[a[dep]][i][now]%P); } void DFS(int u,int fa,int dep) { for (int i=list[u];i;i=g[i].nx) if (g[i].v!=fa) DFS(g[i].v,u,!dep),sz[u]+=sz[g[i].v]; size=0;for (int i=list[u];i;i=g[i].nx) if (g[i].v!=fa) a[++size]=g[i].v; if (sz[u]) { DFS(1,u,dep,0,1ll); for (int i=0;i<=sz[u];i++) f[u][i][!dep]=((C(sz[u],i)-f[u][i][dep])%P+P)%P; } else sz[u]=1,f[u][0][0]=f[u][1][1]=1; } int main() { freopen("game.in","r",stdin); freopen("game.out","w",stdout); scanf("%d",&n); fact[0]=1ll; for (int i=1;i<N;i++) fact[i]=1ll*fact[i-1]*i%P; for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v),Add(v,u); DFS(1,0,0);for (int i=0;i<=sz[1];i++) (ans+=f[1][i][1]*fact[i]%P*fact[sz[1]-i]%P)%=P; printf("%lld",ans); }
在日渐沉没的世界里,我发现了你。