LightOJ1355 Game Of CS (green博弈 扩展)

题目链接:
http://lightoj.com/volume_showproblem.php?problem=1355

题目大意:
给一棵带边权的树,两个人分别给边涂色。边权代表了这条边可以涂色的次数。如果一条边的涂色次数没有用完,那么可以涂他子树的边。无法涂色者为负。

分析:
看到题目的第一想法:搜题解。
然后就发现这个是个没学过的green博弈。
green博弈详解:
https://blog.csdn.net/acm_cxlove/article/details/7854532
这道题就用到了最前面的把一棵树搞成一根竹竿的定理,所以后面的不用看了

可是这道题的边权不都是1。下面分析一下各种边权情况下结点sg值的求法。
首先要知道,根据green博弈的说法,当一个结点下面分叉时,要想办法给它搞成一根竹竿。
对于竹竿来说,每个结点的sg值就是它下面边的个数(边权都=1)。
并且如果知道一个点的sg值为x,那么可以等效认为这个点下面只连着x条边权=1的边。

题解上说如果AB边权值大于1,那么sg[A]=sg[B]^1。
但是没说咋推出来的,所以下面来推一下。

设a[n][x]表示AB边权为n,且B点的sg值为x时结点A的sg值。
1.当前结点下无边(边权=0),此时sg值为0。因为此时没有可以涂色的边了。(a[0][i]=0)
2.边权=1。此时就是green博弈。当前结点的sg值等于他所有子结点sg值+1的异或。(a[1][i]=i+1)
3.边权=2。假设这条边AB下面连着x条边权都=1的边时(即B点sg值=x):
此时求a[2][x]。
当这个边权=2的边下面啥都没有时,只有一种涂色方案:

在这里插入图片描述

其中AB就是当前边权=2的边,B下面没有边了。所以根据第一条,B点的sg值=0,这种涂色方案导致的后继状态就是把AB变成了一个边权=1的边。所以这个后继状态为a[1][0]=0+1=1。所以当前状态的a[2][0]=mex{1}=0。

下面有1条边时(B点sg值=1)有两种后继状态:
在这里插入图片描述
第一种还是把这个边变成了一个边权=1的边,所以此时a[1][1]=1+1=2。
第二种涂完之后就成了上面那个图。此时A点sg值=0。
所以当前状态的a[2][1]=mex{2,0}=1。

下面有两条边时(B点sg值=2)有三种后继状态:
在这里插入图片描述
第一种sg[1][2]=2+1=3;第二种A点sg值=a[2][0]=0;第三种A点sg值=a[2][1]=1。
所以当前状态的a[2][2]=mex{3,0,1}=2。

可以推出,AB边权=2时,a[2][i]=mex{i+1,0,1, … ,i-1}=i。

4.AB边权=3时,按照刚才的过程,当B点sg值=0时,只有一种方案,那就是把AB的边权变成2,所以当前的a[3][0]=mex{0}=1。
当B点sg值=1时,有两种后继状态:
在这里插入图片描述
第一种就是把AB变成了边权=2,且B点sg值=1时的情况,此时sg值=1
第二种就是上面分析的AB=2,B点sg值=0时的情况,此时sg值=1。
所以当前状态的a[3][1]=mex{1,1}=0。

当B点sg值=2时,有三种后继状态:
在这里插入图片描述
可以得出,三种后续状态的A点的sg值分别是2,1,0。
所以当前状态的a[3][2]=mex{2,1,0}=3。
每个状态的后继状态都是之前已经推出的状态。
可得:a[3][i]=mex{a[2][i],a[3][0],a[3][1], … ,a[3][i-1]},因为用到的值都在前面算出,所以可以打表。
可以发现,当AB=3时,若B点sg值是偶数,则A点sg值=B点sg值+1;否则A点sg值=B点sg值-1。

根据上述方法继续递推,可以发现,AB权值n为偶数时,a[n][i]=i(可以理解,AB权值为偶数时只要先手在AB上涂色,那么后手一定可以相应的涂色。所以相当于AB这段没有任何贡献,相当于可以把B下面连的东西直接放到A处。)
AB权值n为奇数时,a[n][i]=i%2==0?i+1:i-1。

以上过程推出了不同权值下结点sg值的规律,所以写代码时就不用这个a数组了。
我们直接用sg[A]数组表示结点A的sg值。
总结一下规律:
当AB权值=1时,sg[A]=sg[B]+1。
当AB权值为偶数时,sg[A]=sg[B]。
当AB权值为奇数时,sg[A]=sg[B]%2==1?sg[B]-1:sg[B]+1。(也就是题解上说的sg[A]=sg[B]^1)
根据这个规律用dfs搜一遍每个结点的sg值,最后根据根结点的sg值判断胜负就行了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define N 1005
#define LL long long

using namespace std;

int sg[N];
vector<vector<pair<int,int> > >v(N);//to,length
void dfs_sg(int now,int father)
{
    for(unsigned int i=0;i<v[now].size();i++)
        if(v[now][i].first!=father)
            dfs_sg(v[now][i].first,now);
    for(unsigned int i=0;i<v[now].size();i++)
        if(v[now][i].first!=father)
        {
            if(v[now][i].second==1)
                sg[now]^=sg[v[now][i].first]+1;
            else if(v[now][i].second%2==0)
                sg[now]^=sg[v[now][i].first];
            else
            {
                if(sg[v[now][i].first]%2)
                    sg[now]^=sg[v[now][i].first]-1;
                else
                    sg[now]^=sg[v[now][i].first]+1;
            }
        }
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        memset(sg,0,sizeof(sg));
        v=vector<vector<pair<int,int> > >(N);
        int n;
        scanf("%d",&n);
        for(int i=0;i<n-1;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            v[a].push_back(make_pair(b,c));
            v[b].push_back(make_pair(a,c));
        }
        dfs_sg(0,0);
        if(sg[0])
            printf("Case %d: Emily\n",tt);
        else
            printf("Case %d: Jolly\n",tt);
    }
    return 0;
}

高哥牛批!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值