专栏文章
题解:P10140 [USACO24JAN] Island Vacation P
P10140题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miq7vb5z
- 此快照首次捕获于
- 2025/12/04 00:23 3 个月前
- 此快照最后确认于
- 2025/12/04 00:23 3 个月前
考虑拆分一次游走过程:我们定义“一次操作”为在 处进行一次判定后发现没有结束,然后从 走到 。
设当前状态下 的出度为 ,则该次操作的贡献为 。
以下 dp 过程中使用“概率”一词似乎并不是非常恰当,我们认为一条路径的“权值”为每次操作的贡献的乘积。自然想到设 表示所有第一次走到 的路径的权值和。
首先建出圆方树。对于一个节点 ,如果我们钦定最后在 节点停下,则我们的过程必然会分为如下阶段:
- 首先我们要走到 ,贡献为 。
- 然后我们可能会穿过 节点子树内的若干个环回到 节点,然后再下一次操作的“判定”时倒闭。
这里我们发现从 出发穿过一个环然后回到 的过程也是很重要的。结合以上分析,我们定义如下状态:
- 表示从 出发,第一次走到 的所有路径权值和。
- 表示从 出发,绕着 对应的点走一圈的路径权值和。 只在方点处有定义。
- 表示从 出发到达 后,在 上绕若干圈之后,再从 出发走一步的路径在 之后的所有操作的权值和。

的转移是最容易的:蓝色环的贡献为 ,红色边的贡献直接计算即可。
现在考虑计算 。
假设我们从 节点出发,选择了 中的方点对应的环,最后回到 节点的贡献差不多长成这个样子:
解释一下意思:每个环的贡献显然就是 ,先乘上,然后考虑修正。
每次从 出发时, 的出度分别为 ,而 里的贡献为 ,所以需要乘上 的修正项; 中元素没有顺序还会带来 的贡献;
你发现我们只在乎选取的集合的 和 ,跑一遍背包,然后带入公式计算即可。
写代码时发现这个式子还需要进行一些修补,比如需要考虑走完之后的那个红色的出边,以及 时的 和其他情况略有不同,需要特别注意。
计算 和 可以自底向上跑一遍 dfs。复杂度 。
接下来考虑如何计算 。

如图。目前已知你第一次走到了 ,则你会先走绿色的环,然后颜色红色边走到 ,途中还可能经过一些蓝色的环。
蓝色的环贡献就是 ,红色边可以直接计算,现在只需要考虑绿色边。
我们发现,可以选的绿色圈的集合 是 子树内所有可以选择的圈的集合 去掉当前这个环,所以跑一个撤销背包即可,式子同上。
至于答案的计算,你只需要把 和 拼起来就行了。
综上,总复杂度为 ,瓶颈在背包。可以用分治 NTT 做到 ,懒得写了,就这样吧。
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();
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...