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;
}