题面
https://www.luogu.org/problem/P4782
题解
输出时,要输出所属强连通分量的字典序最小的点,而且经实测,改了就错。
因为一个联通块,缩了点形成一个$DAG$,我们肯定要输出$DAG$上拓扑序靠后的$DAG$(附加条件更少),有因为$tarjan$是正着搜的,拓扑序靠后的强连通分量先被判定,字典序更小。
#include<cstdio> #include<iostream> #include<vector> #include<stack> #include<cstdlib> using namespace std; int n,m,clo=0,cnt=0; int dfn[2000050],low[2000050],bel[2000050]; vector<int> to[2000050]; stack<int> stk; bool ins[2000050],sat[1000050],vis[2000050]; void tarjan(int x){ int i,y,l=to[x].size(); dfn[x]=low[x]=++clo; ins[x]=true; stk.push(x); for (i=0;i<l;i++) { if (!dfn[to[x][i]]) { tarjan(to[x][i]); low[x]=min(low[x],low[to[x][i]]); } else if (ins[to[x][i]]) { low[x]=min(low[x],dfn[to[x][i]]); } } if (dfn[x]==low[x]) { ++cnt; int opt=(x-1)/n; int num=x-opt*n; do { y=stk.top(); stk.pop(); bel[y]=cnt; ins[y]=false; } while (y!=x); } } void dfs(int x){ if (vis[x]) return; vis[x]=true; if (x>n) sat[x-n]=1; else sat[x]=0; int i,l=to[x].size(); for (i=0;i<l;i++) if (!vis[to[x][i]])dfs(to[x][i]); } int main(){ int i,u,v,opt1,opt2; scanf("%d %d",&n,&m); for (i=1;i<=m;i++) { scanf("%d %d %d %d",&u,&opt1,&v,&opt2); to[(!opt1)*n+u].push_back(opt2*n+v); to[(!opt2)*n+v].push_back(opt1*n+u); } for (i=1;i<=2*n;i++) if (!dfn[i]) tarjan(i); for (i=1;i<=n;i++) if (bel[i]==bel[n+i]) { puts("IMPOSSIBLE"); return 0; } puts("POSSIBLE"); for (i=1;i<=n;i++) if (bel[i]<bel[n+i]) dfs(i); else dfs(n+i); for (i=1;i<=n;i++) printf("%d ",sat[i]); }