2017多校九 05題 hdu 6165 FFF at Valentine 縮點 dp找最長鏈/拓撲排序

題目鏈接

題意

判定一個圖是不是單向連通圖。
// 其實就是poj 2186,不過poj的那道題數據水了些= =
// 瀏覽題目時看成了FFT at Valentine嚇死我= =

思路

先套路一發,tarjan求強聯通分量,縮點,至此預處理完成。(這部分詳細內容煩請移步本菜另一篇 強聯通分量 縮點 tarjan 入門題小集
然後怎麼處理呢?

法一

現在我們得到了一個DAG ,直觀想法就是有沒有一個長鏈串起了圖中所有的點。於是可以模仿一下樹形dp 的做法,dfs 一遍,dep[u]=max{dep[v]+1|v,uv} .

Code

#include <cstdio>
#include <cstring>
#define maxn 1010
#define maxm 6010
#include <stack>
#define inf 0x3f3f3f3f
using namespace std;
stack<int> s;
int dfn[maxn], low[maxn], ne[maxn], ne2[maxn], belong[maxn], ind[maxn], tot, tot2, cnt, scc, in[maxn], kas, dep[maxn], vis[maxn];
bool ok;
struct Edge {
    int from, to, ne;
    Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {}
}edge[maxm], edge2[maxm];
void add(int u, int v) {
    edge[tot] = Edge(u, v, ne[u]);
    ne[u] = tot++;
}
void add2(int u, int v) {
    edge2[tot2] = Edge(u, v, ne2[u]);
    ne2[u] = tot2++;
}
void init() {
    tot = 0;
    memset(ne, -1, sizeof(ne));
}
void init2() {
    tot2 = 0; ok = false;
    memset(vis, 0, sizeof(vis));
    memset(ne2, -1, sizeof(ne2));
    memset(dep, 0, sizeof(dep));
}
void tarjanInit() {
    cnt = scc = 0;
    while (!s.empty()) s.pop();
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(ind, 0, sizeof(ind));
    memset(in, 0, sizeof(in));
}
void tarjan(int u) {
    dfn[u] = low[u] = ++cnt;
    in[u] = true;
    s.push(u);
    for (int i = ne[u]; i != -1; i = edge[i].ne) {
        int v = edge[i].to;
        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]) {
        ++scc;
        while (true) {
            int v = s.top();
            in[v] = false;
            s.pop();
            belong[v] = scc;
            if (v == u) break;
        }
    }
}
void contract() {
    init2();
    for (int i = 0; i < tot; ++i) {
        int u = edge[i].from, v = edge[i].to;
        if (belong[u] == belong[v]) continue;
        add2(belong[u], belong[v]);
        ++ind[belong[v]];
    }
}
void dfs(int u) {
    vis[u] = true;
    dep[u] = 1;
    for (int i = ne2[u]; i != -1; i = edge2[i].ne) {
        int v = edge2[i].to;
        if (!vis[v]) dfs(v);
        dep[u] = max(dep[u], dep[v] + 1);
    }
}
void work() {
    int n, m;
    init(); tarjanInit();
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) tarjan(i);
    }
    contract();
    for (int i = 1; i <= scc; ++i) {
        if (!ind[i]) { dfs(i); if (dep[i] == scc) ok = true; break; }
    }
    if (ok) printf("I love you my love and our love save us!\n");
    else printf("Light my fire!\n");
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}

法二

在新圖中,必定只能有一個入度爲 0 的點,否則走了一個,另一個就沒辦法達到了。
刪去這個點和它指向的邊,圖中剩下的部分中也必定只能有一個入度爲 0 的點,道理同上。
依次類推。
咦?這不就是拓撲排序嗎!

Code

#include <cstdio>
#include <cstring>
#define maxn 1010
#define maxm 6010
#include <stack>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
stack<int> s;
queue<int> q;
int dfn[maxn], low[maxn], ne[maxn], ne2[maxn], belong[maxn], ind[maxn], tot, tot2, cnt, scc, in[maxn], kas, dep[maxn], vis[maxn];
struct Edge {
    int from, to, ne;
    Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {}
}edge[maxm], edge2[maxm];
void add(int u, int v) {
    edge[tot] = Edge(u, v, ne[u]);
    ne[u] = tot++;
}
void add2(int u, int v) {
    edge2[tot2] = Edge(u, v, ne2[u]);
    ne2[u] = tot2++;
}
void init() {
    tot = 0;
    memset(ne, -1, sizeof(ne));
}
void init2() {
    tot2 = 0;
    memset(vis, 0, sizeof(vis));
    memset(ne2, -1, sizeof(ne2));
    memset(dep, 0, sizeof(dep));
}
void tarjanInit() {
    cnt = scc = 0;
    while (!s.empty()) s.pop();
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(ind, 0, sizeof(ind));
    memset(in, 0, sizeof(in));
}
void tarjan(int u) {
    dfn[u] = low[u] = ++cnt;
    in[u] = true;
    s.push(u);
    for (int i = ne[u]; i != -1; i = edge[i].ne) {
        int v = edge[i].to;
        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]) {
        ++scc;
        while (true) {
            int v = s.top();
            in[v] = false;
            s.pop();
            belong[v] = scc;
            if (v == u) break;
        }
    }
}
void contract() {
    init2();
    for (int i = 0; i < tot; ++i) {
        int u = edge[i].from, v = edge[i].to;
        if (belong[u] == belong[v]) continue;
        add2(belong[u], belong[v]);
        ++ind[belong[v]];
    }
}
bool topsort() {
    int zero = 0, idx;
    for (int i = 1; i <= scc; ++i) {
        if (ind[i] == 0) ++zero, idx = i;
    }
    if (zero > 1) return false;
    while (!q.empty()) q.pop();
    q.push(idx);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        zero = 0;
        for (int i = ne2[u]; i != -1; i = edge2[i].ne) {
            int v = edge2[i].to;
            --ind[v];
            if (ind[v] == 0) ++zero, idx = v;
        }
        if (zero > 1) return false;
        if (zero == 1) q.push(idx);
    }
    return true;
}
void work() {
    int n, m;
    init(); tarjanInit();
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) tarjan(i);
    }
    contract();
    if (topsort()) printf("I love you my love and our love save us!\n");
    else printf("Light my fire!\n");
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章