POJ 2762【强联通缩点】【拓扑排序】Going from u to v or from v to u?

来源:互联网 发布:卫生网络答题 编辑:程序博客网 时间:2024/05/16 05:46

题目大意是,判断一个有向图中所有的任意两点x,y,是否满足,从x可以走到y 或者 从y可以走到x

很显然,同一个SCC中的所有点一定满足这个条件,当同一个SCC中的所有点要么同时选要么同时不选的时候,就可以缩点了。缩点的方法就是给每个点加一个强联通分量编号(染色),然后tarjan结束后考察每一条边,若某条边的两头不是同一个颜色,就加一条新边。

缩点后的图一定是一个DAG(有向无环图),在这张新图上可以得到一个结论:若满足题目要求的条件,这个新图是不能有分叉的,因为一旦有了分叉,就会有叶节点,而叶节点是无法走到比其本身更深的点的,同样一个比叶节点更深的点也无法走回叶节点(图是有向的)

现在求的就是这个新图的最长链(如果判断出有分叉直接返回0),如果最长链等于新图点数(因为有可能会出现孤立的点),那么这张图便是可行的。

PS:可能输入给的不只有一个图,所以要加点东西:

for(int i=1; i<=n; i++)    if(!dfn[i]) tarjan(i);
#include <iostream>#include <cstdio>#include <algorithm>#include <stack>#include <queue>#include <cstring>using namespace std;const int maxn = 6010;#define debug(x) cerr << #x << "=" << x << endl;#define mmm(x) memset(x,0,sizeof(x));int t,n,m,last[maxn],tot,l,dfn[maxn],low[maxn],color[maxn],sccnum;int tot_new,last_new[maxn],n_new,chain,cou;int id[maxn], od[maxn];bool ans, flg[maxn];stack <int> s;struct Edge{    int u,v,to;    Edge(){}    Edge(int u, int v, int to): u(u), v(v), to(to) {}}e[maxn];inline void addedge(int u, int v) {    e[++tot] = Edge(u,v,last[u]);    last[u] = tot;}void tarjan(int x) {    dfn[x] = low[x] = ++cou;    s.push(x);    flg[x] = true;    for(int i=last[x]; i; i=e[i].to) {        if(!dfn[e[i].v]) {            tarjan(e[i].v);            if(low[e[i].v] < low[x]) low[x] = low[e[i].v];        } else if(dfn[e[i].v] < low[x] && flg[e[i].v]){            low[x] = dfn[e[i].v];        }    }    if(dfn[x]==low[x]) {        int fr,num;        sccnum++; //新图中的点数         do {            fr = s.top();            s.pop();            flg[fr] = 0;//出栈了             num++;            color[fr] = sccnum;        }while(fr != x);    }}struct Edge_new{    int u,v,to;    Edge_new(){}    Edge_new(int u, int v, int to): u(u), v(v), to(to) {}}e_new[maxn];inline void addedge_new(int u, int v) {    e_new[++tot_new] = Edge_new(u,v,last_new[u]);    last_new[u] = tot_new;} queue <int> q;bool topo() {    for(int i=1; i<=sccnum; i++) {        if(id[i] == 0)             q.push(i);    }    while(!q.empty()) {        int u = q.front();        q.pop();        if(!q.empty()) return false; //如果是单链,队列中一定是进一个出一个,如果队列中同时存在多个元素就说明有图分叉         for(int i=last_new[u]; i; i=e_new[i].to) {            int v = e_new[i].v;            id[v]--;            if(!id[v]) q.push(v);        }    }    return true;}bool solve() {    for(int i=1; i<=tot; i++) {        int u = e[i].u, v = e[i].v;        if(color[u] != color[v]) {            addedge_new(color[u],color[v]);            od[color[u]]++;            id[color[v]]++;        }    }    if(topo()) return true;    else return false;}int main() {    scanf("%d",&t);    while(t--) {        mmm(dfn);mmm(low);mmm(color);mmm(last);mmm(flg);        mmm(last_new);mmm(e);mmm(e_new);mmm(id);mmm(od);        tot = 0;cou = sccnum = tot_new = chain = 0;        while(!s.empty())            s.pop();        while(!q.empty())            q.pop();            ans = 0;        scanf("%d %d", &n, &m);        for(int i=1; i<=m; i++) {            int u, v;            scanf("%d %d", &u, &v);            addedge(u,v);        }        for(int i=1; i<=n; i++) {            if(!dfn[i]) tarjan(i);        }        if(solve()) printf("Yes\n");        else printf("No\n");    }    return 0;}
阅读全文
0 0