P9220 「TAOI-1」椎名真昼 题解
因为笔者最近在刷 Tarjan 题,并且刚好在看邻家天使,所以就来写此题了。
总之是细节非常多的一道分类讨论题。
题意
给定一张有向图,图中每个节点被染成了黑色和白色,Alice 和 Bob 轮流进行操作,每次选择一个点并反转其能到达的所有点的颜色(包括自身),当某一方操作结束后图中所有点都为白色则获胜,问谁能赢。
思路
有一个很显然的结论,如果二人在 \(2\) 回合内都没有必胜策略,则必定平局,因为只要一回合不能达到必胜,另一人就可以把他上一回合进行的操作重置。
首先考虑平局的情况,注意到如果一个强连通分量中有点颜色不同,则必定不可能全部翻转成白色,因为一个强连通分量中的点翻转任意一个都会导致其他的点也翻转。
考虑在什么情况下 Alice 会赢,由于要一步完成,所以 Alice 必须选定一个黑点,并且这个黑点所能到达的点包含所有的黑点,并且其中不含白点。
再来考虑什么情况下 Bob 会赢:
- 当整张图都是白色时,Bob 必胜,因为无论 Alice 怎么翻转,Bob 都能在下回合转回去,实现全白。
- 定义源点表示缩点后入度为 \(0\) 的点,当所有源点都为黑色时,只有源点数量为 \(2\) 且整个图中只有这两个点,Bob 必胜。因为如果有更多点,则 Alice 可以随意操作非源点的点,来破坏全白。
- 如果只有一个源点且为黑色,并且图中只剩 \(2\) 个点,那么只有存在黑(源点)->白这种情况时,Bob必胜。因为这种情况下 Alice 无论怎么翻,Bob 都能在下一步翻成全白。
然后就是要注意细节了,下面是我踩过的几个坑:
- 多测一定要清空,注意在提前退出的地方也要清空
- 写这种题目一定要时刻记好自己在干什么,不要误用变量名
- 判定 Bob 获胜时记得一定要判定图中是否只剩两个点了,而不是判定是否有两个源点
Code
笔者的代码很丑,因为太难调了...
重在理解思路
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200010;
int T,n,m,cnt,tim,reach;
int scc[N],low[N],dfn[N],c[N],ind[N];
bool in[N],col[N],vis[N],bad;
vector<int> g[N],ng[N],dis;
stack<int> st;
void tarjan(int u){
low[u]=dfn[u]=++tim;
st.push(u);
in[u]=1;
for(auto v:g[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(in[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
c[cnt]=-1;
while(!st.empty()){
int x=st.top();
st.pop();
in[x]=0;
scc[x]=cnt;
if(c[cnt]==-1) c[cnt]=col[x];
else if(col[x]!=c[cnt]) bad=1;
if(x==u) break;
}
}
}
void dfs(int u){
if(vis[u]) return;
if(!c[u]){
bad=1;
return;
}
vis[u]=1;
reach++;
for(auto v:ng[u]) dfs(v);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>col[i];
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
if(bad){
cout<<"N";
for(int i=1;i<=n;i++) g[i].clear(),scc[i]=0,dfn[i]=0,low[i]=0,c[i]=0,ind[i]=0,in[i]=0,col[i]=0,vis[i]=0;
for(int i=1;i<=cnt;i++) ng[i].clear();
dis.clear();
while(!st.empty()) st.pop();
cnt=tim=bad=0;
continue;
}
bad=0;
for(int u=1;u<=n;u++) for(auto v:g[u]) if(scc[u]!=scc[v]) ng[scc[u]].push_back(scc[v]);
for(int i=1;i<=cnt;i++){
sort(ng[i].begin(),ng[i].end());
ng[i].erase(unique(ng[i].begin(),ng[i].end()),ng[i].end());
}
for(int i=1;i<=cnt;i++) for(auto v:ng[i]) ind[v]++;
queue<int> q;
for(int i=1;i<=cnt;i++) if(!ind[i]) q.push(i);
bool win=0;
int fu=-1,cntb=0;
for(int i=1;i<=cnt;i++) if(c[i]) cntb++;
while(!q.empty()){
int u=q.front();
q.pop();
if(c[u]){
fu=u;
break;
}
for(auto v:ng[u]){
ind[v]--;
if(!ind[v]) q.push(v);
}
}
if(fu!=-1){
reach=0,bad=0;
memset(vis,0,sizeof(vis));
dfs(fu);
if(!bad&&reach==cntb) win=1;
}
if(win){
cout<<"A";
for(int i=1;i<=n;i++) g[i].clear(),scc[i]=0,dfn[i]=0,low[i]=0,c[i]=0,ind[i]=0,in[i]=0,col[i]=0,vis[i]=0;
for(int i=1;i<=cnt;i++) ng[i].clear();
dis.clear();
while(!st.empty()) st.pop();
cnt=tim=bad=0;
continue;
}
memset(ind,0,sizeof(ind));
for(int i=1;i<=cnt;i++) for(auto v:ng[i]) ind[v]++;
for(int i=1;i<=cnt;i++) if(!ind[i]) dis.push_back(i);
win=0;
bool white=0;
for(auto i:dis) if(!c[i]) white=1;
if(!white){
if(cnt==2) win=1;
if(dis.size()==1&&cnt==2&&!c[ng[dis[0]][0]]) win=1;
}
else{
bool aw=0;
for(int i=1;i<=cnt;i++) if(c[i]) aw=1;
if(!aw) win=1;
}
if(!win) cout<<"N";
else cout<<"B";
for(int i=1;i<=n;i++) g[i].clear(),scc[i]=0,dfn[i]=0,low[i]=0,c[i]=0,ind[i]=0,in[i]=0,col[i]=0,vis[i]=0;
for(int i=1;i<=cnt;i++) ng[i].clear();
dis.clear();
while(!st.empty()) st.pop();
cnt=tim=bad=0;
}
return 0;
}
完结撒花

浙公网安备 33010602011771号