2017多校九 05题 hdu 6165 FFF at Valentine 缩点 dp找最长链/拓扑排序

34 篇文章 0 订阅
11 篇文章 0 订阅

题目链接

题意

判定一个图是不是单向连通图。
// 其实就是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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值