zoukankan      html  css  js  c++  java
  • 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);
        }
    }
    
  • 相关阅读:
    23.Java函数
    22.java中的break和continue关键字
    21.Java中for循环
    20.java中的while循环
    19.java中选择判断语句(switch)
    18.java中判断(if…else)
    17.java运算符的优先级与结合性
    16.Java三元运算符
    数组去重
    数组 字符串 对象 常用方法
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12498144.html
Copyright © 2011-2022 走看看