傳送門
首先可以看出這是一道博弈論的題,我們考慮對於每一棵樹求出sg。
存在這樣兩種情況:
1.去掉根,那麼這棵樹的sg是其所有點的sg異或和。
2.去掉子樹中的乙個點,那麼這棵樹的sg是其所有點的sg異或和再異或上這個點到根這條路徑上的所有點sg。
對於第一種情況很好處理,而第二種情況暴力做法則是列舉每個點,每次對應的sg不同。
證明可見這篇部落格
考慮優化:trie樹合併。
把sg轉化為二進位制數,方便找到mex。
對於當前這棵樹
考慮自底向上推出sg值,用trie維護當前子樹中,刪去一條從根開始的鏈後得到的每種情況的sg值(即為與這條鏈相鄰的子樹的sg值的異或和)構成的集合,於是可以查詢mex,通過trie的合併可以構建出當前點的父親對應的集合,另外要通過打標記實現整棵trie異或上乙個值(也就是這條鏈的異或和)。
#include#define m 100003view code#define n 4000003
using
namespace
std;
intread()
while(s>='
0'&&s<='9')
return x*f;
}struct
edgew[m*2
];int tot=0
,ndnum;
intsiz[n],head[m],tag[n],lc[n],rc[n],vis[m],sg[n],rt[n];
void
init()
void add(int a,int
b)void pushdown(int k,int d)//
d是二進位制的位數
int merge(int x,int y,int
d)void insert(int &k,int x,int
d)
if(d==-1)return
; pushdown(k,d);
insert((((
1<1
); siz[k]=siz[lc[k]]+siz[rc[k]];
}int query(int k,intd)
void dfs(int x,int
fa)
//刪除子樹裡的節點就相當於是子樹里這種對應的情況再異或上外邊子樹的sg。但是我們不可能用一般的方法來存一棵子樹裡所有的sg
//處理子樹之後,把它合併上來,就能得到當前節點的所有拓展局面的sg了
for(int i=head[x];i;i=w[i].nextt)//
不同的情況合併進去
insert(rt[x],res,
20);
sg[x]=query(rt[x],20);//
在dfs中處理出了sg值
}int
main()
int ans=0
;
for(int i=1;i<=n;++i)
if(!vis[i])ndnum=0,dfs(i,0),ans^=sg[i];
if(ans)printf("
alice\n");
else printf("
bob\n");}}
bzoj4730 Alice和Bob又在玩遊戲
題目鏈結 給出乙個森林,兩個人輪流操作,每次把乙個節點以及它的祖先全部抹去,無節點可以抹去是算輸,問是否存在先手必勝策略。trie樹合併,其實就是線段樹合併。bzoj4134的簡單版 二進位制小心寫錯 bzoj4730 include include include include include ...
4730 Alice和Bob又在玩遊戲
time limit 40 sec memory limit 1024 mb submit 116 solved 52 submit status discuss alice和bob在玩遊戲。有n個節點,m條邊 0 m n 1 構成若干棵有根樹,每棵樹的根節點是該連通塊內編號最 小的點。alice和...
Alice和Bob賭糖果 概率 經典
alice和bob賭糖果。規則是這樣的 alice從 l,r 中隨機抽乙個數,bob從 l,r 中隨機抽乙個數,誰抽的數大誰就贏,輸的一方給另一方1顆糖 平局不用給糖 他們會一直賭下去直到有一方沒有糖果為止。alice有n顆糖果,bob有m顆糖果,求alice將bob的糖果贏完的概率。第一行3個整數...