[CEOI2020] 星际迷航
一、题目
二、解法
考虑单向边的意义,如果接上来的是必胜点,那么显然对胜负状态没有影响的。如果接上来的是必败点,那么如果原来的必败点状态会翻转,否则还是必胜态。
但总之,我们只需要关心接上来的点的必胜点还是必败点,接上来的图是什么样子根本不重要。
那么设 \(A_i\) 表示 \(i\) 个版本所有情况下的必败点总数,\(B_i\) 表示 \(i\) 个版本所有情况下的必胜点总数,设 \(f_u\) 表示点 \(u\) 的初始状态,\(g_u\) 表示有多少种连接方案(指在树上接一个必败点)可以让 \(u\) 的初始状态改变:
\[A_i\leftarrow A_{i-1}\cdot \sum_u([f_u=1]g_u+[f_u=0](n-g_u))+B_{i-1}\cdot n\cdot \sum_u[f_u=0]
\]
\[B_i\leftarrow A_{i-1}\cdot \sum_u([f_u=0]g_u+[f_u=1](n-g_u))+B_{i-1}\cdot n\cdot\sum_{u} [f_u=1]
\]
很容易用矩阵乘法优化,所以关键是求出 \(f,g\),可以简单讨论一下:
- 若 \(f_u=0\),则翻转自己或者任意一个儿子都可以,\(g_u=1+\sum_{v}[f_v=1]\cdot g_v\)
- 若 \(f_u=1\) 且必败的儿子只有一个,\(g_u=\sum_v[f_v=0]g_v\)
所以可以换根,时间复杂度 \(O(n+\log d)\)
#include <cstdio>
#include <vector>
using namespace std;
const int M = 100005;
const int MOD = 1e9+7;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,d,f[M],g[M],g0[M],g1[M],cnt[M];vector<int> G[M];
struct mat
{
int a[2][2];
mat() {a[0][0]=a[1][0]=a[0][1]=a[1][1]=0;}
mat operator * (const mat &b) const
{
mat r;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%MOD;
return r;
}
}A,F;
void dfs1(int u,int fa)
{
cnt[u]=f[u]=g0[u]=g1[u]=g[u]=0;
for(int v:G[u]) if(v^fa)
{
dfs1(v,u);
if(!f[v]) cnt[u]++,g0[u]+=g[v];
else g1[u]+=g[v];
}
f[u]=(cnt[u]>0);
if(!f[u]) g[u]=g1[u]+1;
else if(cnt[u]==1) g[u]=g0[u];
}
void dfs2(int u,int fa)
{
cnt[u]=f[u]=g0[u]=g1[u]=g[u]=0;
for(int v:G[u])
{
if(!f[v]) cnt[u]++,g0[u]+=g[v];
else g1[u]+=g[v];
}
f[u]=(cnt[u]>0);
if(!f[u]) g[u]=g1[u]+1;
else if(cnt[u]==1) g[u]=g0[u];
//
if(f[u]==1)
{
F.a[0][1]++;
A.a[0][0]=(A.a[0][0]+g[u])%MOD;
A.a[0][1]=(A.a[0][1]+n-g[u])%MOD;
A.a[1][1]=(A.a[1][1]+n)%MOD;
}
else
{
F.a[0][0]++;
A.a[0][1]=(A.a[0][1]+g[u])%MOD;
A.a[0][0]=(A.a[0][0]+n-g[u])%MOD;
A.a[1][0]=(A.a[1][0]+n)%MOD;
}
//
int tf=f[u],tg=g[u],tc=cnt[u];
for(int v:G[u]) if(v^fa)
{
if(!f[v])
{
if(cnt[u]==1)
cnt[u]=0,g[u]=g1[u]+1,f[u]=0;
else//f[u]=1;
{
cnt[u]--;
g[u]=(cnt[u]==1)?g0[u]-g[v]:0;
}
}
else if(!f[u]) g[u]=g1[u]-g[v]+1;
dfs2(v,u);
f[u]=tf;g[u]=tg;cnt[u]=tc;
}
}
signed main()
{
n=read();d=read()-1;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1,0);dfs2(1,0);
while(d>0) {if(d&1) F=F*A;A=A*A;d>>=1;}
int ans=g[1]*F.a[0][0]%MOD;
if(f[1]) ans=(n*(F.a[0][0]+F.a[0][1])-ans)%MOD;
printf("%lld\n",(ans+MOD)%MOD);
}