鏈接:https://ac.nowcoder.com/acm/contest/5531/F
來源:牛客網
牛牛的樹行棋
時間限制:C/C++ 2秒,其他語言4秒
空間限制:C/C++ 524288K,其他語言1048576K
64bit IO Format: %lld
題目描述
牛牛和牛妹是一對好朋友,這天他們倆決定在樹上玩一個遊戲。
遊戲的名字是“樹行棋”,規則如下:
給定一個含有n個節點分別從1到n編號且以節點1爲根的樹,一開始每個節點各有1個棋子。
牛牛和牛妹輪流進行操作,且牛牛先手移動。
每次操作可以選擇將任意一個棋子移動到其子樹中的任意一個節點,但是每次移動必須保證棋子的位置發生變化(不能拿起再放回原處)。
直到無法進行操作,輪到不能操作的一方即爲敗者。
現在假設雙方都採用最優策略,請問牛牛能否贏。
如果牛牛(先手)能夠贏,請問牛牛第一步有多少種不同的必勝策略。
我們認爲兩個策略是不同的,當且僅當這兩個策略一開始選擇的棋子不同,或者移動的路徑不同。
輸入描述:
第一行輸入一個n,(1≤n≤5×105)n,(1 \leq n \leq 5 \times 10^5)n,(1≤n≤5×105),表示樹含有n個點。
接下來n-1行,每行輸入一個x,y,(1≤x,y≤n1\leq x,y \leq n1≤x,y≤n)表示x與y相連。
輸出描述:
對於每組數據,如果牛牛(先手)能贏則輸出‘YES’,否則輸出‘NO’。
如果能贏,請在第二行跟着輸出一個正整數ans,表示第一步的必勝策略數目。
示例1
輸入
複製3 1 2 1 3
3
1 2
1 3
輸出
複製YES 2
YES
2
說明
因爲2,3兩個節點爲葉節點,題目要求每次操作棋子的位置必須產生變化,所以2,3兩節點的棋子是無法移動的。
只能操作節點1上的棋子,可以選擇將其移動到2或者移動到3,所以共2種必勝決策。
示例2
輸入
複製4 1 2 2 3 3 4
4
1 2
2 3
3 4
輸出
複製NO
NO
說明
一上來牛牛有6種移動方案:
1、將1號節點上的棋子移動到2
此時2號節點有兩個棋子,3號節點4號節點各有1個棋子,且4號節點的棋子無法移動。
後手只要選擇3號節點的棋子將其移動到4號節點,接下來後手完全模仿先手的操作就可以勝利。
...(剩下5種方案由於類似的原因都是後手取得勝利)
備註:
注意每次操作是將一個棋子移動至子樹中的任意一個節點,並不是移動到直接兒子。
思路:
一個狀態的 SG 函數值,就是其所有後繼狀態(進行一次操作後所到達的狀態)
,其中STAx爲x的後繼狀態。
有以下兩個結論:
① 若當前局面的 SG 函數值爲 0,先手必敗,否則先手必勝。
② 若一個遊戲是多個獨立遊戲組成的,那麼當前局面的 SG 函數值是多個獨立遊戲 SG 函數值的異或 (^)。
對於這道題,顯然每個棋子都是獨立的。也就是每個棋子都構成了一個獨立遊戲。因此當前局面的SG函數值就是每個棋子的SG函數值的異或和。
可以推出,葉子結點的SG函數值爲0,葉子結點的父節點的SG函數值爲1,以此類推,結點u的SG函數值就是以u爲根的子樹中,與u離的最遠的那個葉子結點的距離。因此,可以O(n)的進行一遍dfs以求出所有節點的SG函數值(記它們的異或和爲val)。
若val=0,則先手必敗,否則先手必勝。下面計算勝利的方案數。
假設結點u的SG函數值爲x[u],那麼只要把它移動到其子樹中SG函數值爲x[u]^val的節點,那麼新的局面的SG函數值爲val^x[u]^x[u]^val=0,這樣就讓對手進入了必敗態。差分一下,可以O(n)求出答案。
注意:記錄SG函數值爲x[u]的節點個數時,要注意數組的大小。因爲異或可以使val的值超過n!!!
代碼:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=5e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k,ct;
int a[maxn],x[maxn],cnt[maxn<<2];
int he[maxn],ans,val;
struct node{
int v,nxt;
}e[maxn<<1];
template <typename T>
inline void read(T &X)
{
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
void add(int u,int v){
e[ct].v=v;
e[ct].nxt=he[u];
he[u]=ct++;
}
void dfs(int u,int fa){
x[u]=0;
for(int i=he[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
x[u]=max(x[u],x[v]+1);
}
val^=x[u];
}
void dfs1(int u,int fa){
ans-=cnt[x[u]^val];
cnt[x[u]]++;
for(int i=he[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
dfs1(v,u);
}
ans+=cnt[x[u]^val];
}
int main()
{
int T,cas=1;
//read(T);
//while(T--)
{
read(n);
rep(i,1,n) he[i]=-1;
rep(i,1,n-1){
int x,y;
read(x);read(y);
add(x,y);
add(y,x);
}
dfs(1,-1);
dfs1(1,-1);
if(val) printf("YES\n%d\n",ans);
else puts("NO");
}
return 0;
}