[SMOJ1830]小岛

来源:互联网 发布:义乌样品淘宝拍摄 编辑:程序博客网 时间:2024/04/28 06:39

题目描述

N 个城市,编号 1 至 N,农夫 FJ 为这 N 个城市共设计了 M 条单向飞机航班。
如果存在两个不同的城市 ab,使得城市 a无论如何也无法到达城市 b(即使转机也不能到达b),而且城市 b 无论如何也无法到达城市 a(即使转机也不能到达a),
那么我们认为 FJ 设计的这 M 条飞机航班是不合理的,输出”No”,否则输出“Yes”。

输入格式 1830.in

多组测试数据。
第一行,一个整数 R ,表示有 R 组测试数据。
每组测试数据格式:
第一行,两个整数,NM1N500001M60000
接下来有 M 行,每行两个整数: ab,表示城市 a 有一条单向航班飞向城市 b

输出格式 1830.out

R 行,每行输出Yes或者No。

输入样例 1830.in

1
3 3
1 2
2 3
3 1

输出样例 1830.out

Yes


题意:有 n 个结点和 m 条有向边,判断是否存在两个点 uv,满足 u 无法到达 v v 无法到达 u。事实上,就是判断图是否弱连通。

如果题目条件是“u 无法到达 v 或者 v 无法到达 u”,那么显然只要判断整个图是否为一个强连通分量即可。但是这样做是错的,很容易举出反例。
但是,显然缩点这一步并不是没有用的。缩点后同一强连通分量内的点肯定互相可达,因此只需要判断强连通分量之间是否可达就可以了。

我们不妨这样来思考:把缩点后得到的 DAG 中结点编号为 1~n,按编号从小到大顺次排成一行,容易发现:每条边一定是从编号小的往编号大的方向连,绝对不可能倒着。请重视这个性质,这是解决本题的关键。

那么,我们考虑第 n1 个点和第 n 个点。如果它们之间不连通,则整个图就是根本不合法的。想要使他们连通,只有一种可能,就是存在一条从 n1 连到 n 的有向边。不可能往前连再连回来,这不符合我们上面得出的性质。
同理,对于第 n2 个点和第 n1 个点,要使它们连通,必须存在一条从 n2 连到 n1 的有向边…………对于第 1 个点和第 2 个点,必须存在从 1 到 2 的边。

可能会问,这样一来,仅仅是满足了相邻结点之间连通的要求而已。但其他的呢?事实上,只要能够满足相邻结点之间连通,任意两个结点之间就一定连通。
归纳起来,也就是说,要求缩点后得到的是一条链!但是,除了相邻结点中的边之外,可能存在其他多余的边。那无所谓,只要成一条链,已经足够满足要求。

具体的实现:缩点可以在 O(n+m) 内跑一遍 tarjan,之后可以用 topo 做,每次删去入度为 0 的点。如果一次同时删去多于 1 个点,则不合法。否则删到最后,就成一条链,一定合法。至于为什么能够这样做,最好还是手画一下进行分析。

lgj 也在评讲的时候告诉我们:赛场上思考问题往往不一定能够立即得到正解,因此要多举例,画图(或列式?)分析,找到一些共通点,从而突破。

参考代码:

#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <stack>#include <queue>using namespace std;const int maxn = 1e5 + 100;const int maxm = 1e5 + 100;struct Edge { int to, next; } edge[maxm];int a[maxm], b[maxm];int n, m;int head[maxn], belong[maxn];int cntEdge, cntScc;void addEdge(int u, int v) {    edge[++cntEdge].to = v;    edge[cntEdge].next = head[u];    head[u] = cntEdge;}int dfn[maxn], low[maxn], timeStamp;bool instack[maxn];stack <int> st;int inDegree[maxn];void tarjan(int root) {    dfn[root] = low[root] = ++timeStamp;    instack[root] = true;    st.push(root);    for (int i = head[root]; i; i = edge[i].next)        if (!dfn[edge[i].to]) {            tarjan(edge[i].to);            low[root] = min(low[root], low[edge[i].to]);        } else if (instack[edge[i].to]) low[root] = min(low[root], dfn[edge[i].to]);    if (dfn[root] == low[root]) {        cntScc++;        int cur;        do {            cur = st.top(); st.pop();            instack[cur] = false;            belong[cur] = cntScc; //每个结点属于缩点后的哪个强连通分量        }        while (cur != root);    }}bool topo() {    queue <int> q;    while (!q.empty()) q.pop();    for (int i = 1; i <= cntScc; i++)        if (!inDegree[i]) q.push(i); //将入度为 0 的点入队    if (q.size() > 1) return false; //如果一次有多个点同时入队,则它们之间就互不可达,整个图不合法    while (!q.empty()) {        int cur = q.front(); q.pop();        for (int i = head[cur]; i; i = edge[i].next)            if (!(--inDegree[edge[i].to])) q.push(edge[i].to);        if (q.size() > 1) return false; //同上    }    return true;}int main(void) {    freopen("1830.in", "r", stdin);    freopen("1830.out", "w", stdout);    int r;    scanf("%d", &r);    while (r--) {        scanf("%d%d", &n, &m);        memset(head, 0, sizeof head);        cntEdge = 0;        for (int i = 0; i < m; i++) {            scanf("%d%d", &a[i], &b[i]);            addEdge(a[i], b[i]);        }        memset(dfn, 0, sizeof dfn);        timeStamp = 0;        memset(instack, false, sizeof instack);        cntScc = 0;        for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);        memset(head, 0, sizeof head);        cntEdge = 0;        memset(inDegree, 0, sizeof inDegree);        for (int i = 0; i < m; i++)            if (belong[a[i]] != belong[b[i]]) {                addEdge(belong[a[i]], belong[b[i]]);                ++inDegree[belong[b[i]]];            }        puts(topo() ? "Yes" : "No");    }    return 0;}


1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 口红颜色太艳了怎么办 我在菲律宾想走怎么办 当国家流通货币不够用怎么办 苹果5s账号密码忘记怎么办 钢铁雄心3补给不足怎么办 灯外观颜色太难看了怎么办 被移民公司骗了怎么办 文明5大包锁区怎么办 鸭子被黄鼠狼叼走了怎么办 黄鼠狼再店了拉屎怎么办 我只有信用卡但是又想去嫖怎么办 瑞士退税单掉了怎么办 装了新风噪音大怎么办 意大利 护照被偷了怎么办 请问去意大利要怎么办护照 在意大利护照丢了怎么办 考研二战档案打回原籍怎么办 脸上的肉往下掉怎么办 眼镜带了往下掉怎么办 孩子捅别的孩子眼睛了怎么办 眼睛不小心捅伤怎么办 我的爸爸是小偷怎么办 违停罚款忘记交怎么办 顺风车无人接单怎么办 来例假腰特别疼怎么办 把人撞死了全责怎么办 朝鲜与美合作对付中国怎么办 申请美国大学gpa不够怎么办 武装突袭3有地雷怎么办 辐射4狗肉跟丢了怎么办 洛奇英雄传死绑S怎么办 在老挝遇到坏人带枪怎么办 买了sd卡卡槽塞不下怎么办 现役军人家庭被邻居欺服怎么办 地铁买票买多了怎么办 免税店买的东西转机怎么办 绿能电动车坏了怎么办? 永久单车收不到验证码怎么办 24速山地车档乱了怎么办 新电瓶车被偷了怎么办 汽车前风挡玻璃砸出洞怎么办