Gym102431G Game on the Tree

Link
结论:后手必胜当且仅当根节点为直径中点。

根节点为直径中点:
如果两人都在直径上移动,那么不论先手如何移动,后手总能移动到对称的位置。
如果先手移出了直径,设这一点与根节点的距离为(x),那么后手仍然回到直径上距离根节点(x)的点。
其它的情况先手只需移动至直径中点(当直径中点在边上时移动至该边的一端),然后按照上面的策略即可。

那么我们现在要求的就是直径中点为根节点的连通块个数。
(f_{u,i})表示(u)的子树中最大深度为(i)的子图的方案数,长链剖分优化dp即可。

#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
const int N=200007,P=1000000007;
int n,son[N],dep[N];std::vector<int>e[N];
struct node{int val,tag;node(){val=0,tag=1;}}pool[N*4],*now=pool,*f[N],g[N];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int add(int a,int b){return inc(a,b),a;}
int mul(int a,int b){return 1ll*a*b%P;}
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
void dfs(int u,int fa){for(int v:e[u])if(v^fa)if(dfs(v,u),dep[v]+1>dep[u])dep[u]=dep[son[u]=v]+1;}
void alloc(int u){f[u]=now,now+=dep[u]+2;}
void modify(node*a,int x){a->tag=mul(a->tag,x),a->val=mul(a->val,x);}
void pushdown(node*a){if(a->tag^1) modify(a+1,a->tag),a->tag=1;}
void dp(int u,int fa)
{
    if(f[u][0].val=1,!son[u]) return ;
    f[son[u]]=f[u]+1,dp(son[u],u);
    for(int v:e[u])
	if(v^fa&&v^son[u])
	{
	    alloc(v),dp(v,u);int len=dep[v]+1;
	    if(u^1)
	    {
		int r=1,s=1;
		for(int i=1;i<=len;++i) pushdown(f[u]+i),pushdown(f[v]+i-1),inc(r,f[u][i].val),f[u][i].val=add(mul(r,f[v][i-1].val),mul(s,f[u][i].val)),inc(s,f[v][i-1].val);
		modify(f[u]+len+1,s);
	    }
	    else
	    {
		int r=1,s=1,t=0;
		for(int i=1,t1,t2;i<=len;++i)
		{
		    pushdown(f[u]+i),pushdown(g+i),pushdown(f[v]+i-1);
		    t1=add(mul(add(r,t),f[v][i-1].val),mul(s,f[u][i].val));
		    t2=add(mul(s,g[i].val),mul(add(g[i].val,f[u][i].val),f[v][i-1].val));
		    inc(r,f[u][i].val),inc(s,f[v][i-1].val),inc(t,g[i].val);
		    f[u][i].val=t1,g[i].val=t2; 
		}
		modify(f[u]+len+ 1,s),modify(g+len+1,s);
	    }
	}
}
int main()
{
    for(int t=read(),Case=1,ans;Case<=t;++Case)
    {
	n=read(),ans=1,memset(dep+1,0,n<<2),memset(son+1,0,n<<2),std::fill(g,g+n+1,node()),std::fill(pool,now,node()),now=pool;
	for(int i=1;i<=n;++i) e[i].clear();
	for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	dfs(1,0),alloc(1),dp(1,0);
	for(int i=1;i<=n;++i) pushdown(g+i),inc(ans,g[i].val);
	printf("Case #%d: %d
",Case,ans);
    }
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12498144.html