POJ 3710:Christmas Game

Description

在树上挂几个环,每一个环只与树有一个公共点,环与环之间无公共边,每次删掉一条边,把不与根节点相连的部分删除,不能操作者输,问是否先手必胜。
题面

Solution

由于环是在叶子上的,所以这个环可以单独考虑
假设这个环是奇环,那么砍掉任意一条边之后,就会变成从叶子节点延伸出来的两条链,且这两条链奇偶性相同,所以后继状态的 \(SG\) 异或起来不可能为奇数,并且可以为 \(0\),所以 \(SG=mex(SG[u])=1\)
假设这个环是偶环,那么这两条链的奇偶性不同,所以异或起来的所有情况中一定没有 \(0\),所以 \(SG=mex=0\)
那么我们就可以把环缩成一个点了
假设是偶环,实际上就没有贡献了,直接把它去掉就可以了
假设是奇环,那么会有 \(1\) 的贡献,我们直接在这个叶子节点后面接上一个点就可以达到这样的效果了

那么现在问题就转化为了树上删边游戏,利用结论:节点 \(x\)\(SG\) 等于所有儿子 \(u\)\((SG[u]+1)\) 的异或和
这样就推出了根节点的 \(SG\)
因为有很多棵树,再把所有根节点的 \(SG\) 异或起来就行了

还有就是这个题仿佛是可以有点双的?

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int N=2010;
int n,m,T,sz[N],head[N],to[N*2],nxt[N*2],num=1;bool vis[N];
int dfn[N],low[N],DFN=0,st[N],top=0,sum,q[N],r=0;
inline void Clear(){
    num=1;DFN=sum=0;
    for(register int i=0;i<N;i++)sz[i]=head[i]=dfn[i]=low[i]=vis[i]=0;
}
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
inline void tarjan(int x,int last){
    dfn[x]=low[x]=++DFN;st[++top]=x;
    for(int i=head[x];i;i=nxt[i]){
        if(i==last || to[i]>n)continue;
        int u=to[i];
        if(!dfn[u]){
            tarjan(u,i^1),low[x]=min(low[x],low[u]);        
            if(low[u]>=dfn[x]){
                int sz=2;r=0;
                while(top && st[top]!=u)q[++r]=st[top--],sz++;
                if(top)q[++r]=st[top--];
                if(sz>2)while(r)vis[q[r--]]=1;
                if(sz>2 && (sz&1))link(x,++sum);
            }
        }
        else low[x]=min(low[x],dfn[u]);
    }
}
inline int dfs(int x,int last){
    int ret=0;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];
        if(u==last || vis[u])continue;
        ret^=(dfs(u,x)+1);
    }
    return ret;
}
inline int work(){
    Clear();
    int x,y;
    scanf("%d%d",&n,&m);sum=n;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        link(x,y);link(y,x);
    }
    tarjan(1,0);
    return dfs(1,1);
}
int main(){
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    while(~scanf("%d",&T)){
        int ans=0;
        while(T--)ans^=work();
        if(ans)puts("Sally");
        else puts("Harry");
    }
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/8666926.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值