题解:P10140 [USACO24JAN] Island Vacation P

· · 题解

考虑拆分一次游走过程:我们定义“一次操作”为在 u 处进行一次判定后发现没有结束,然后从 u 走到 v

设当前状态下 u 的出度为 d,则该次操作的贡献为 \dfrac {(1-p_u)} d

以下 dp 过程中使用“概率”一词似乎并不是非常恰当,我们认为一条路径的“权值”为每次操作的贡献的乘积。自然想到设 f_u 表示所有第一次走到 u 的路径的权值和。

首先建出圆方树。对于一个节点 u,如果我们钦定最后在 u 节点停下,则我们的过程必然会分为如下阶段:

  1. 首先我们要走到 u,贡献为 f_u
  2. 然后我们可能会穿过 u 节点子树内的若干个环回到 u 节点,然后再下一次操作的“判定”时倒闭。

这里我们发现从 u 出发穿过一个环然后回到 u 的过程也是很重要的。结合以上分析,我们定义如下状态:

现在考虑计算 $h_u$。 假设我们从 $u$ 节点出发,选择了 $S$ 中的方点对应的环,最后回到 $u$ 节点的贡献差不多长成这个样子: $$\frac {d_0^{|S|} d_0!!|S|!}{(d_0-2|S|)!!}\prod_{v\in S} g_v$$ 解释一下意思:每个环的贡献显然就是 $g_v$,先乘上,然后考虑修正。 每次从 $u$ 出发时,$u$ 的出度分别为 $d_0,d_0-2,d_0-4,\cdots$,而 $g_v$ 里的贡献为 $\frac 1 d$,所以需要乘上 $\frac d {d_0-2k}$ 的修正项;$S$ 中元素没有顺序还会带来 $|S|!$ 的贡献; 你发现我们只在乎选取的集合的 $|S|$ 和 $\prod g_v$,跑一遍背包,然后带入公式计算即可。 写代码时发现这个式子还需要进行一些修补,比如需要考虑走完之后的那个红色的出边,以及 $u=1$ 时的 $d_0$ 和其他情况略有不同,需要特别注意。 计算 $g_u$ 和 $h_u$ 可以自底向上跑一遍 dfs。复杂度 $O(n^2)$。 - - - 接下来考虑如何计算 $f_u$。 ![](https://cdn.luogu.com.cn/upload/image_hosting/k7uxhvq8.png) 如图。目前已知你第一次走到了 $u$,则你会先走绿色的环,然后颜色红色边走到 $v$,途中还可能经过一些蓝色的环。 蓝色的环贡献就是 $h_x$,红色边可以直接计算,现在只需要考虑绿色边。 我们发现,可以选的绿色圈的集合 $S_0$ 是 $u$ 子树内所有可以选择的圈的集合 $S$ 去掉当前这个环,所以跑一个撤销背包即可,式子同上。 至于答案的计算,你只需要把 $f_u$ 和 $h_u$ 拼起来就行了。 综上,总复杂度为 $O(n^2)$,瓶颈在背包。可以用分治 NTT 做到 $O(n \log ^2 n)$,懒得写了,就这样吧。 ```cpp #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int N=20009; const int mod=1e9+7; int inv[N]; int n,ncnt=0,a[N],deg[N]; vector<int> to[N]; int siz[N]; namespace Build{ vector<int> G[N]; int m; int dfn[N],low[N],dfncnt=0; int sta[N],tp=0; void dfs(int u,int f) { dfn[u]=low[u]=++dfncnt; sta[++tp]=u; for(int v:G[u]) if(!dfn[v]) { dfs(v,u); low[u]=min(low[u],low[v]); if(low[v]==dfn[u]) { ncnt++; to[ncnt].push_back(u); to[u].push_back(ncnt); siz[ncnt]=1; for(int x=0;x!=v;tp--) { x=sta[tp]; to[ncnt].push_back(x); to[x].push_back(ncnt); siz[ncnt]++; } } } else low[u]=min(low[u],dfn[v]); } void ReadIn() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n*2;i++) to[i].clear(),G[i].clear(),siz[i]=0; for(int i=1,u,v;i<=m;i++) { scanf("%d%d",&u,&v); G[u].push_back(v),G[v].push_back(u); } for(int i=1;i<=n*2;i++) dfn[i]=low[i]=0; dfncnt=0,tp=0; for(int i=1;i<=n;i++) deg[i]=G[i].size(); ncnt=n; dfs(1,0); } } using Build::ReadIn; int g[N],h[N]; void dfs1(int u,int f) { if(u>n) { g[u]=2ll*(mod+1-a[f])*inv[deg[f]]%mod; for(int v:to[u]) if(v!=f) { dfs1(v,u); g[u]=1ll*g[u]*h[v]%mod; } } else { for(int v:to[u]) if(v!=f) dfs1(v,u); vector<int> tmp; tmp.push_back(1); for(int v:to[u]) if(v!=f&&siz[v]>2) { tmp.push_back(0); for(int i=(int)(tmp.size())-1;i;i--) tmp[i]=(1ll*tmp[i-1]*g[v]+tmp[i])%mod; } int d1=deg[u],d2=deg[u]-(f!=0),s1=(mod+1-a[u])%mod; for(int i=0;i<tmp.size();i++) { h[u]=(h[u]+1ll*tmp[i]*s1%mod*inv[d2])%mod; s1=1ll*s1*d1%mod*inv[d2]%mod*(i+1)%mod; d2-=2; } } } int f[N],ans[N]; void dfs2(int u,int fa) { vector<int> tmp; tmp.push_back(1); for(int v:to[u]) if(v!=fa&&siz[v]>2) { tmp.push_back(0); for(int i=(int)(tmp.size())-1;i;i--) tmp[i]=(1ll*tmp[i-1]*g[v]+tmp[i])%mod; } int d1=deg[u], d2=deg[u]-(fa!=0), s1=1; for(int i=0;i<tmp.size();i++) { ans[u]=(ans[u]+1ll*tmp[i]*(d2==0?1:a[u])%mod*s1)%mod; s1=1ll*s1*d1%mod*inv[d2]%mod*(i+1)%mod; d2-=2; } ans[u]=1ll*ans[u]*f[u]%mod; for(int vv:to[u]) if(vv==fa) continue; else if(siz[vv]==2) { for(int v:to[vv]) if(v!=u) { d1=deg[u], d2=deg[u]-(fa!=0), s1=1ll*f[u]*(mod+1-a[u])%mod; for(int i=0;i<tmp.size();i++) { f[v]=(f[v]+1ll*tmp[i]*s1%mod*inv[d2])%mod; s1=1ll*s1*d1%mod*inv[d2]%mod*(i+1)%mod; d2-=2; } dfs2(v,vv); } } else { vector<int> tmp2=tmp; for(int i=1;i<tmp.size();i++) tmp[i]=(tmp[i]+1ll*(mod-g[vv])*tmp[i-1])%mod; tmp.pop_back(); int coaf=1ll*f[u]*(mod+1-a[u])%mod; for(int i=1;i<to[vv].size();i++) { d1=deg[u], d2=deg[u]-(fa!=0), s1=coaf; for(int j=0;j<tmp.size();j++) { (f[to[vv][i]]+=1ll*tmp[j]*s1%mod*inv[d2]%mod)%=mod; s1=1ll*s1*d1%mod*inv[d2]%mod*(j+1)%mod; d2-=2; } coaf=1ll*coaf*h[to[vv][i]]%mod; } coaf=1ll*f[u]*(mod+1-a[u])%mod; for(int i=(int)(to[vv].size())-1;i>=1;i--) { d1=deg[u], d2=deg[u]-(fa!=0), s1=coaf; for(int j=0;j<tmp.size();j++) { (f[to[vv][i]]+=1ll*tmp[j]*s1%mod*inv[d2]%mod)%=mod; s1=1ll*s1*d1%mod*inv[d2]%mod*(j+1)%mod; d2-=2; } coaf=1ll*coaf*h[to[vv][i]]%mod; } swap(tmp,tmp2); for(int v:to[vv]) if(v!=u) dfs2(v,vv); } } void work() { ReadIn(); for(int i=1;i<=ncnt;i++) f[i]=g[i]=h[i]=ans[i]=0; dfs1(1,0); f[1]=1; dfs2(1,0); for(int i=1;i<=n;i++) printf("%d ",ans[i]); puts(""); } signed main() { int T; scanf("%d",&T); inv[0]=inv[1]=1; for(int i=2;i<=20000;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; while(T--) work(); } ```