Game on Tree

原题链接

题解

easy.ver::只能朝一个方向走,还剩奇数个格子时先手获胜

medium.ver:\(u_i\) 为根节点,这样就只能朝子节点的方向走,设 \(dp[now]\) 为当以now为根的树,且now节点已经有一颗棋(其子节点均还没有)时,先手必胜1还是必败0,状态转移方程:\(dp[now]|=(1-dp[next])\)

hard.ver: 考虑沿用medium的想法,并用换根法(因为当前节点只和子节点有关)
假如已知以节点 \(w\) 为根的树的所有节点的胜负情况
那么对于其子节点 \(v\) 而言换根后

  • \(dp[w]=0\)\(dp[v]=1\) 不会变
  • \(dp[w]=1\)\(dp[v]=0\ or\ 1\) 但是如果 \(w\) 只有 \(v\) 一个节点0,那么此时修改 \(dp[w]=0,dp[v]=1\) 不然不变

code

#include<bits/stdc++.h>
using namespace std;
vector<int> G[200005];
int dp[200005]={0};
int cnt[200005]={0};
int ans[200005]={0};
void dfs(int now,int fa)
{
    for(auto next:G[now])
    {
        if(next==fa) continue;
        dfs(next,now);
        dp[now]|=(1-dp[next]);//dp[now] 的意思是当以now为根的树,且now节点已经有一颗棋(其子节点均还没有)时,先手必胜1还是必败0
        cnt[now]+=(1-dp[next]);
    }
}

void alter(int now,int fa)
{
    ans[now]=dp[now];
    int temdp=dp[now],temcnt=cnt[now];
    for(auto next:G[now])
    {
        if(next==fa) continue;
        if(dp[now]==0) cnt[next]++;
        else if(dp[now]==1)
        {
            if(dp[next]==0)
            {
                if(cnt[now]==1)
                {
                    dp[now]=0;
                    dp[next]=1;
                    cnt[next]++;
                }
                cnt[now]--;
            }
        }
        alter(next,now);

        dp[now]=temdp;
        cnt[now]=temcnt;//这个复原工作充分体现了本次换根法的当前节点只与其相邻节点有关
    }
}
int main()
{
    int n,t;
    cin>>n>>t;
    for(int i=1;i<n;i++)
    {
        int x,y;
        cin>>x>>y;
        G[x].push_back(y);
        G[y].push_back(x);
    }


    dfs(1,1);
    alter(1,1);
    while(t--)
    {
        int x;
        cin>>x;
        puts(ans[x]?"Ron":"Hermione");
    }
    return 0;
}

posted @ 2024-05-14 20:43  纯粹的  阅读(136)  评论(0)    收藏  举报