牛客练习赛63 F.牛牛的树行棋

牛牛的树行棋

https://ac.nowcoder.com/acm/contest/5531/F

题目大意:
给定一棵树,每个结点上都有一个棋子,牛牛和牛妹两个人每次选择任意一个结点的棋子,将棋子移动到该节点子树下的节点位置。(不可以不移动)牛牛先手,牛妹后手。
现在假设双方都采用最优策略,请问牛牛能否赢。
如果牛牛(先手)能够赢,请问牛牛第一步有多少种不同的必胜策略。
我们认为两个策略是不同的,当且仅当这两个策略一开始选择的棋子不同,或者移动的路径不同。

参考:Lskkkno1 大佬题解 (由于写的急,万分抱歉

https://blog.nowcoder.net/n/6650ed32a8d64f649dd0b370eb22edaa

分析:
前提知识:
SG 函数定义与结论:(粗略解释)
任何一个公平组合游戏都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边.
g(x)表示x局面的SG值, g(x)=mex { g(y) | y是x的后继 }表示x局面内的子局面SG值集合中最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{1,3,5}=0、mex{}=0.
当前SG值为0,先手必输,否则后手必输.
游戏的和的SG函数值是它的所有子游戏的SG函数值的异或.-------因为每个棋子的移动是独立的,那么游戏的SG值等于所有棋子的SG值的异或和.

这道题套用SG函数.
考虑决策叶子节点的棋子,叶子节点的棋子无法移动,所以SG函数为0.
一个结点的子节点只有叶子节点,那么该节点的的SG值为1.
一个结点的所有子节点的中最大SG值为1,那么该子节点的后继节点肯定有0的,那么当前节点的SG值为2.
...
可以推导出,一个结点的SG值等于所有子节点的最大SG值加一.

我们先判断先手是否能必胜,直接计算所有节点的SG值的异或和是否为零.
若先手必胜,考虑计算必胜决策方案个数.
先手必赢的决策: 游戏的SG值为图片说明 值不为0,先手要把SG值变为0,那么对于结点x,SG值为图片说明 ,我们只需要将该节点的棋子移动到SG值为图片说明 的子节点即可。
那么我们可以dfs处理出所有节点的SG值。
因为每个结点只能移动到该子树中的结点,可以用树上差分进行计算方案。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=5e5+10;

vector<int> g[maxn];
int cnt[maxn<<2];
int mx[maxn];
ll ans;
ll res;

void dfs1( int x,int f )
{
    mx[x]=0;
    for( auto v:g[x] )
    {
        if( v==f ) continue;
        dfs1(v,x);
        mx[x]=max(mx[x],mx[v]+1);
    }
    res^=mx[x];
}

void dfs2( int x,int f )
{
    if( mx[x]>(mx[x]^res) ) ans-=cnt[mx[x]^res];
    cnt[mx[x]]++;
    for( auto v:g[x] )
    {
        if( v==f ) continue;
        dfs2(v,x);
    }
    if( mx[x]>(mx[x]^res) ) ans+=cnt[mx[x]^res];
}

int main()
{
    int n;
    cin>>n;

    for( int i=1;i<n;i++ )
    {
        int u,v;cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u); 
    }
    dfs1(1,0);
    dfs2(1,0);
    puts( res ? "YES" : "NO" );
    if( res ) cout<<ans<<endl;
} 
全部评论
如果对别人的博客进行了参考,请加上 reference
1
送花
回复
分享
发布于 2020-05-10 10:52
和lskknol的博客写的好像诶
1
送花
回复
分享
发布于 2020-05-10 10:58
滴滴
校招火热招聘中
官网直投

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务