题意:
给一棵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;
}