码迷,mamicode.com
首页 > 其他好文 > 详细

Gym102431G Game on the Tree

时间:2020-03-15 16:27:10      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:val   时移   main   移动   pool   etc   type   深度   include   

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

根节点为直径中点:
如果两人都在直径上移动,那么不论先手如何移动,后手总能移动到对称的位置。
如果先手移出了直径,设这一点与根节点的距离为\(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\n",Case,ans);
    }
}

Gym102431G Game on the Tree

标签:val   时移   main   移动   pool   etc   type   深度   include   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12498144.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!