Atcoder Beginner Contest 209 E Shiritori
阿新 • • 發佈:2021-07-12
Atcoder Beginner Contest 209 E Shiritori
題目連結:https://atcoder.jp/contests/abc209/tasks/abc209_e
很容易想到這是一幅圖。我們將每個單詞的前三個字母向後三個字母連一條邊。然後就構成了一幅有向的圖。對於這幅圖來說,如果一個人走到了一個沒有出度的點,那麼他就贏了,因為另一個人不能再說出任何一個單詞(走到另外一個點)。我們用一個 \(dp\) 陣列表示從這個點開始遊戲的輸贏情況,0 表示平局,1 表示 Takahashi
贏,2 表示 Aoki
贏。那麼顯然,所有出度為 0 的點的 \(dp\) 值都為1。
現在我們考慮 \(dp\)
Aoki
只要往這個點上走,那麼他就贏了。如果這個點能到達的所有點的 \(dp\) 值為 2,那麼這個點的 \(dp\) 值才能為 1。如果一個點不符合上述的兩種情況,那麼它的 \(dp\) 值就是 0 了,因為能更新狀態為 1,2,的只有上述兩種情況,然後這個點一定是 0,1,2 中的一個,只有可能是 0了。
然後我們反向連邊,將所有入度為 0 的點的 \(dp\) 值設定為 1。然後跑一趟拓撲排序就好了。當然,這裡的拓撲排序要魔改一下,如果一個點的 \(dp\) 值被確定為 1 或 2 了,可以立馬扔進佇列中,因為根據我們上面的轉移,它不可能再被更改了。這樣操作可以讓拓撲排序滲透到一些環中。對於一些環來說,環的某一部分是可以被更新到的,一部分又是無法被更新到的,而可以被更新到的點中它們被更新的狀態是不同的,簡而言之,一個環中所有點的 \(dp\)
程式碼如下:
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5+5; int n,dp[MAXN],in[MAXN],tot,p[MAXN]; char s[10]; map <string,int> mp; vector <int> edge[MAXN]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%s",s+1); int len=strlen(s+1); string u="",v=""; for(int j=1;j<=3;++j) u+=s[j]; for(int j=len-2;j<=len;++j) v+=s[j]; if(!mp.count(u)) mp[u]=++tot; if(!mp.count(v)) mp[v]=++tot; p[i]=mp[v];in[mp[u]]++; edge[mp[v]].push_back(mp[u]); } queue <int> q; for(int i=1;i<=tot;++i) { if(!in[i]) { dp[i]=1; q.push(i); } } while(!q.empty()) { int now=q.front(); q.pop(); for(int i=0;i<edge[now].size();++i) { int to=edge[now][i]; if(dp[to]==0) { in[to]--; if(dp[now]==1) dp[to]=2,q.push(to); else if(in[to]==0) { dp[to]=1; q.push(to); } } } } for(int i=1;i<=n;++i) { if(dp[p[i]]==1) printf("Takahashi\n"); else if(dp[p[i]]==2) printf("Aoki\n"); else printf("Draw\n"); } }
像這種題目,平局的轉移不好判定,那麼我們將另外兩種判定出來,其他情況就是平局了。換句話說,如果一個集合的子集不好求,我們可以求這個子集的補集,從而得到這個子集。這在某些題目中也是一種優秀的方法。
路漫漫其修遠兮,吾將上下而求索。