【HDU - 5963】朋友(博弈,思维,必胜态必败态,找规律)
題干:
B君在圍觀一群男生和一群女生玩游戲,具體來說游戲是這樣的:
給出一棵n個節點的樹,這棵樹的每條邊有一個權值,這個權值只可能是0或1。 在一局游戲開始時,會確定一個節點作為根。接下來從女生開始,雙方輪流進行 操作。
當一方操作時,他們需要先選擇一個不為根的點,滿足該點到其父親的邊權為1; 然后找出這個點到根節點的簡單路徑,將路徑上所有邊的權值翻轉(即0變成1,1 變成0 )。
當一方無法操作時(即所有邊的邊權均為0),另一方就獲得了勝利。
如果在雙方均采用最優策略的情況下,女生會獲勝,則輸出“Girls win!”,否則輸 出“Boys win!”。
為了讓游戲更有趣味性,在每局之間可能會有修改邊權的操作,而且每局游戲指 定的根節點也可能是不同的。
具體來說,修改邊權和進行游戲的操作一共有m個,具體如下:
??“0 x”表示詢問對于當前的樹,如果以x為根節點開始游戲,哪方會獲得勝利。
??“1 x y z ”表示將x和y之間的邊的邊權修改為z。
B君當然知道怎么做啦!但是他想考考你。
Input
包含至多5組測試數據。
第一行有一個正整數,表示數據的組數。
接下來每組數據第一行,有二個空格隔開的正整數n,m,分別表示點的個數,操 作個數。保證n,m< 40000。
接下來n-1行,每行三個整數x,y,z,表示樹的一條邊。保證1<x<n, 1<y< n, 0 <= z <= 1。
接下來m行,每行一個操作,含義如前所述。保證一定只會出現前文中提到的兩 種格式。
對于操作0,保證1 <= x <= n ;對于操作1,保證1 <= x <= n, 1 <= y <= n, 0 <= z <= 1,保證樹上存在一條邊連接x和y。
Output
對于每組數據的每一個詢問操作,輸出一行“Boys win!”或者“Girls win!”。
Sample Input
2 2 3 1 2 0 0 1 1 2 1 1 0 2 4 11 1 2 1 2 3 1 3 4 0 0 1 0 2 0 3 0 4 1 2 1 0 0 1 0 2 0 3 1 3 4 1 0 3 0 4Sample Output
Boys win! Girls win! Girls win! Boys win! Girls win! Boys win! Boys win! Girls win! Girls win! Boys win! Girls win!解題報告:
想想他需要不停的換根,數據量又如此龐大,以至于每次換根后,我dfs一遍的時間都沒有,所以肯定是個找規律題,肯定不會給你遍歷整棵樹的機會的。從必勝態必敗態的角度考慮,不難發現如果是一條鏈的情況,規律就是根節點相連的那條邊的邊權如果是1,那女生勝,反之男生勝。又因為如果是多條鏈的話,鏈之間互不影響,所以可以直接統計和根節點相鄰的邊的邊權和,如果是奇數,那就是女生獲勝,反之男生獲勝。當然如果先一條鏈然后分成兩條鏈的情況,那其實也問題不大,因為發現可以轉化成一條鏈的情況上面。(其實可以大膽猜想,既然一條鏈的情況,只和根節點相鄰的邊的邊權有關系,假設這條邊是e,那也就是所有 想到達根節點的路徑,只要需要經過邊e,那就都和路徑上的其他邊沒關系,之和邊e相關,所以就這樣?)
好吧,還是來一發正解:
正解是,經分析發現無論操作哪個節點,這個節點都會使其所在子樹中與根直接相連的那條邊翻轉。那么再根據游戲的規則以及子樹的性質,會發現若你面對當前這條與根相連的邊的權值為1時,對方通過子樹上任意點來翻轉當前邊,你都能再次進行翻轉。如果你面對權值為0,那么要么你不能進行操作,要么不管你進行什么操作對方都還能進行操作。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define FF first #define SS second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e5 + 5; vector<PII> vv[MAX]; int n,m; int main() {int T;cin>>T;while(T--) {scanf("%d%d",&n,&m);for(int i = 1; i<=n; i++) vv[i].clear(); for(int x,y,z,i = 1; i<=n-1; i++) {scanf("%d%d%d",&x,&y,&z);vv[x].pb(pm(y,z));vv[y].pb(pm(x,z));}for(int i = 1; i<=n; i++) sort(vv[i].begin(),vv[i].end());int op,u,v,z,ans;while(m--) {scanf("%d",&op);if(op == 0) {ans=0; scanf("%d",&u);for(auto x : vv[u]) {if(x.SS == 1) ans++;}if(ans & 1) puts("Girls win!");else puts("Boys win!");}else {scanf("%d%d%d",&u,&v,&z);int pos1 = lower_bound(vv[u].begin(),vv[u].end(),pm(v,-1)) - vv[u].begin();int pos2 = lower_bound(vv[v].begin(),vv[v].end(),pm(u,-1)) - vv[v].begin();vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);}}} return 0 ; }關于實現細節:
發現對于0操作,如果是菊花圖的話就炸了。
所以優化一下,發現不需要枚舉:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define FF first #define SS second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 2e5 + 5; vector<PII> vv[MAX]; int n,m,in[MAX]; int main() {int T;cin>>T;while(T--) {scanf("%d%d",&n,&m);for(int i = 1; i<=n; i++) vv[i].clear(),in[i]=0; for(int x,y,z,i = 1; i<=n-1; i++) {scanf("%d%d%d",&x,&y,&z);vv[x].pb(pm(y,z));vv[y].pb(pm(x,z));if(z == 1) in[x]++,in[y]++; }for(int i = 1; i<=n; i++) sort(vv[i].begin(),vv[i].end());int op,u,v,z,ans;while(m--) {scanf("%d",&op);if(op == 0) {ans=0; scanf("%d",&u);if(in[u] & 1) puts("Girls win!");else puts("Boys win!");}else {scanf("%d%d%d",&u,&v,&z);int pos1 = lower_bound(vv[u].begin(),vv[u].end(),pm(v,-1)) - vv[u].begin();int pos2 = lower_bound(vv[v].begin(),vv[v].end(),pm(u,-1)) - vv[v].begin();if(vv[u][pos1].SS==0 && z == 1) {vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);in[u]++,in[v]++;}else if(vv[u][pos1].SS==1 && z == 0) {vv[u][pos1] = pm(v,z); vv[v][pos2] = pm(u,z);in[u]--,in[v]--;} }}} return 0 ; }?
總結
以上是生活随笔為你收集整理的【HDU - 5963】朋友(博弈,思维,必胜态必败态,找规律)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 申请信用卡可以取消吗 银行电话回访是好时
- 下一篇: 申请信用卡会给直系亲属打电话吗 一般不会