P6970 [NEERC2016] Game on Graph
P6970 [NEERC2016] Game on Graph
[NEERC2016] Game on Graph
题面翻译
Gennady 和 Georgiy 在玩一个有向图上的游戏。这个图有
你要对于每个点,分别求出以这个店为起点开始游戏,两人分别作为先手,最终会输,赢,还是平局(游戏无限循环)。
其中,Gennady 因为玩得很开心,所以他更期望将游戏变为平局;Georgiy 还有很多其他事,所以他更期望游戏不要平局。当然,在不平局的基础上,两人都更希望赢。
输入格式
第一行两个数
接下来
输出格式
两行,第一行表示分别以每一个点为起点 Gennady 先手的胜负情况;第二行表示分别以每一个点为起点 Georgiy 先手的胜负情况。W
表示赢,L
表示输,D
表示平局。
by a___
(1≤n≤100000)
(1≤m≤200000)
Time limit: 2 s, Memory limit: 512 MB.
---------------------------------------------------------------------------------------
将每个点拆成A,B两类;
对于一个点u及其邻居v:
用opt=0代表A类节点,opt=1代表B类节点
对于A类点v.A:
如果它的所有u.A已经被遍历过了,那就搜索v.a这个节点。
(因为A希望平局,所以要先确定他邻居点的状态再确定他)
对于B类点v.B:
如果它没有被访问过,那就访问v.B这个节点
(因为B不希望平局,所以只要能访问就访问)
注意:在主程序中用for遍历启动搜索时,只有那些d==0的节点才能被搜
(即状态已经确定的节点)
然后,我们在第一次搜索完之后统计所有节点的胜负
而对于胜负的判定:(对于第一次搜素)
若对于 vis[0][opt]=1:
说明其被访问过即度数已经为0则A输了,反之,暂定A赢了
若对于 vis[1][opt]=1:
说明其被访问过即度数已经为0则B赢了,反之,暂定B输了
(因为B在第一次dfs中为后手,所以要反过来)
再进行第二次搜索,而在两次搜索中都未被确定的节点就是平局了
然后这题就做完了
Code:
#include<bits/stdc++.h> const int N=1e5+5; using namespace std; vector<int> E[N]; int d[N][2],vis[N][2]; char ans[2][N]; int n,m; void dfs(int u,int opt) { vis[u][opt]=1; for(int v : E[u]) { d[v][opt^1]--; if(opt==0&&vis[v][1]==0)dfs(v,1);//对于B类点,只要先前没遍历过,就搜一下 if(opt==1&&d[v][0]==0)dfs(v,0);//对于A类点,要等到所有邻居被访问过之后才能搜 } } void work() { cin>>n>>m; for(int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); E[y].push_back(x);//建反图,由已知确定未知 d[x][1]++,d[x][0]++;//两类点的度数 } for(int i=1;i<=n;i++) { if(d[i][0]==0&&vis[i][0]==0) { dfs(i,0); } } for(int i=1;i<=n;i++) { if(vis[i][0]) { ans[0][i]='L'; } else { ans[0][i]='W'; } if(vis[i][1]) { ans[1][i]='W'; } else { ans[1][i]='L'; } } for(int i=1;i<=n;i++) { if(!d[i][1]&&!vis[i][1])dfs(i,1); } for(int i=1;i<=n;i++) { if(!vis[i][1])ans[1][i]='D'; if(!vis[i][0])ans[0][i]='D'; } for(int opt=0;opt<2;opt++) { for(int i=1;i<=n;i++) { putchar(ans[opt][i]); } cout<<"\n"; } } int main() { //freopen("P6970.in","r",stdin);//freopen("P6970.out","w",stdout); work(); }