题解
这题思维难度比较大_
首先,对任意集合 ,定义函数 为不管B怎样A都能进入 的起点的集合, 同理
设充电站集合为 ,则如果起点在 以外B必胜(即B一定有一种策略使得火车无法进入 )
如果 为全集则A赢(不管怎样A总能使火车进入 )
如果 不为全集,设 为 的补集,则如果起点属于 则B必胜
如果起点属于 中B一定可以进入 ,所以B同样必胜
剩下的节点胜负未定,我们可以抠掉这些B胜的节点,继续判断 是否为全集并重复上述过程,直到 为全集为止,此时剩下的节点就是A必胜的起点
现在问题就是快速求 , (由于求 的过程和求 的过程相似,所以这里只讨论求 的方法)
由于 ,所以可以根据定义直接求
首先 至少是
每次选一个点,它进入 的条件是:
- 它由A掌控,并且它至少有一条道路通向
- 它由B掌控,并且它所有道路全部都通向
具体实现时可以每次在 中取出一个尚未取出的节点,并更新每个点是否满足条件。
举个栗子
红色节点属于A,蓝色节点属于B,黄色标记代表这是一个充电车站
首先
按照前面的方法求出
则
把 抠掉,得到
用类似的方法,得 , 为全集
所以得到答案
我在交的时候忘删了调试语句,结果调了好久…qwq
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int n,m,q[N],h,t,vis[N];
vector<int> e[N],e2[N];
vector<int> f(int flag,vector<int> a,vector<int> r,vector<int> res){
memset(vis,0,sizeof(vis));
vector<int> ans(n),deg(n);
t=0,h=-1;
for(int i=0;i<n;i++)if(r[i]&&res[i])q[++h]=i,ans[i]=1;
for(int i=0;i<n;i++){for(int j=0;j<(int)e[i].size();j++)if(res[e[i][j]]){if(a[i]^flag)deg[i]++;else deg[i]=1;}}
while(t<=h){
int v=q[t++];
for(int i=0;i<(int)e2[v].size();i++){
int u=e2[v][i];
if(!ans[u]&&res[u]){deg[u]--;if(!deg[u])ans[q[++h]=u]=1;}
}
}
return ans;
}
vector<int> who_wins(vector<int> a,vector<int> r,vector<int> u,vector<int> v){
n=a.size(),m=u.size();
while(m--)e[u[m]].push_back(v[m]),e2[v[m]].push_back(u[m]);
vector<int> ans(n);
for(int i=0;i<n;i++)ans[i]=1;
while(1){
int flag=1;
vector<int> res1=f(1,a,r,ans);
for(int i=0;i<n;i++)if(ans[i]&&!res1[i])flag=0;
if(flag)return ans;
for(int i=0;i<n;i++)res1[i]^=1;
vector<int> res2=f(0,a,res1,ans);
for(int i=0;i<n;i++)if(res2[i])ans[i]=0;
}
return ans;
}