[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);
}
posted @ 2022-03-04 10:40  C202044zxy  阅读(119)  评论(0编辑  收藏  举报