F Christmas Game

F Christmas Game

题意:

给一棵n个节点树,每个点上都有权值,两个人轮流操作,每次可以将一个点的权值给他的父亲节点,(父亲节点与当前点的深度差必须为k),当有一方不能操作时即为输掉。问依次以n个点为根的情况下,先手能否赢

题解:

博弈论
我第一反应是尼姆博弈
我们把节点相对于根的深度分为奇数和偶数
我们这里说的步数是指一个节点上的所能走的步数,因为每次走的长度是固定的(必须向上走深度为k)
如果把一些权值从一个偶数步移动到奇数步,那么对面可以重复一样的行为,这样输的一定是先手(因为后手可以模仿先手)
所以先手想赢必须走奇数步,换句话说偶数步不会对比赛结果有影响可以舍弃
奇数步是如何定义的呢?我们刚才说了和k有很大的关系,如果当前节点x的深度为dep,x的奇偶性是dep/x(向下取整)
我们现在拿到所有奇数步的结果怎么判断胜负?
经典博弈问题NIM游戏
a1⊕a2⊕…⊕an不为零时,当前玩家有一个获胜策略。
总结一下:
如果奇数步上所有值的xorsum不为零,则先手获胜获胜策略。

代码:

我一开始写的dfs直接暴力超时了

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
    
    
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
const int maxn=1e5+8;
vector<int>edge[maxn];
int a[maxn];
int root;
int dep[maxn];
int cnt=0;
int x=0;
int n,k;
inline void dfs(int fa,int now){
    
    
	dep[now]=dep[fa]+1;
	if((dep[now]/k)&1)x^=a[now];
	for(int i=0;i<edge[now].size();i++)
	{
		int v=edge[now][i];
		if(v!=fa)dfs(now,v);
	}
}
//void solve(int now){
//	int x=0;
//	int y=0;
//	int i,j=2*k;
//	int last=k;
//	for(i=k;i<=n;i+=k)
//	{
//		for(j=last+1;j<=i;j++)
//		{
//			for(int k=1;k<=n;k++)
//				if(a[j]==k)
//				{
//					
//				}
//		}
//		last=i;
//	}
////	for(int i=k+1;i<=cnt;y++,i++)
////	{
////		for(int j=1;j<=n;j++)
////			if(a[j]==i)
////			{
////				if(y&1)
////				{
////					x^=a[j];
////				}
////				else 
////				{
////					continue;
////				}
////			}
////	}
//}
int main()
{
	
	//cin>>n>>k;
	n=read();
	k=read();
	for(int i=1;i<n;i++)
	{
		int u,v;
		u=read();
		v=read();
		//cin>>u>>v;
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		root=i;
		x=0;
		memset(dep,0,sizeof(dep));
		cnt=0;
		dep[0]=-1;
		dfs(0,root);
		if(x==0)cout<<0<<" ";
		else cout<<1<<" ";
	}
	return 0;
}

然后我在想简化的话就不能对每个点跑一次dfs,跑一次+换根是不是可以?然后我看了看别人的代码。。。没看啥意思

#include <cstdio>
#include <vector>
using namespace std;
vector<int> mp[100005];
int n,k;
int a[100005];
int dp[100005][45];
int ans[100005][45];
// 
void dfs(int x,int fa)
{
    
    
    dp[x][0]=a[x];
    for(int i=0;i<mp[x].size();i++)
        if(mp[x][i]!=fa)
        {
    
    
            dfs(mp[x][i],x);
            for(int j=0;j<2*k;j++)
            //mp[x][i]是x的第i个子节点 
                dp[x][j]^=dp[mp[x][i]][(j+2*k-1)%(2*k)];
        }
}
void dfss(int x,int fa)
{
    
    
    for(int i=0;i<mp[x].size();i++)
        if(mp[x][i]!=fa)
        {
    
    
            for(int j=0;j<2*k;j++)
            {
    
    
            	//mp[x][i]是x的第i个子节点 
            	ans[mp[x][i]][j]=(ans[x][(j+2*k-1)%(2*k)] ^ dp[mp[x][i]][(j+2*k-2)%(2*k)] ^ dp[mp[x][i]][j]);
			} 
                
            dfss(mp[x][i],x);
        }
}
int main()
{
    
    
    int u,v,now;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n-1;i++)
    {
    
    
        scanf("%d%d",&u,&v);
        mp[u].push_back(v);
        mp[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    dfs(1,0);
    for(int i=0;i<2*k;i++)
        ans[1][i]=dp[1][i];
    dfss(1,0);
    for(int i=1;i<=n;i++)
    {
    
    
        now=0;
        for(int j=k;j<2*k;j++)
            now^=ans[i][j];
        if(now)
            now=1;
        if(i!=n)
            printf("%d ",now);
        else printf("%d\n",now);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35975367/article/details/115426186