HDU 6165 FFF at Valentine (tarjan缩点+拓扑判任意两点联通)


FFF at Valentine

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 786    Accepted Submission(s): 391


Problem Description

At Valentine's eve, Shylock and Lucar were enjoying their time as any other couples. Suddenly, LSH, Boss of FFF Group caught both of them, and locked them into two separate cells of the jail randomly. But as the saying goes: There is always a way out , the lovers made a bet with LSH: if either of them can reach the cell of the other one, then LSH has to let them go.
The jail is formed of several cells and each cell has some special portals connect to a specific cell. One can be transported to the connected cell by the portal, but be transported back is impossible. There will not be a portal connecting a cell and itself, and since the cost of a portal is pretty expensive, LSH would not tolerate the fact that two portals connect exactly the same two cells.
As an enthusiastic person of the FFF group, YOU are quit curious about whether the lovers can survive or not. So you get a map of the jail and decide to figure it out.
 

Input
Input starts with an integer T (T≤120), denoting the number of test cases.
For each case,
First line is two number n and m, the total number of cells and portals in the jail.(2≤n≤1000,m≤6000)
Then next m lines each contains two integer u and v, which indicates a portal from u to v.
 

Output
If the couple can survive, print “I love you my love and our love save us!”
Otherwise, print “Light my fire!”
 

Sample Input
  
  
3 5 5 1 2 2 3 2 4 3 5 4 5 3 3 1 2 2 3 3 1 5 5 1 2 2 3 3 1 3 4 4 5
 

Sample Output
  
  
Light my fire! I love you my love and our love save us! I love you my love and our love save us!
 

Source
 

Recommend
liuyiding
 


  • 题意:

    • 给定一个有向图,n个点,m条边,无自环,无重边
    • 问:是否任意两点A,B,满足AB连通
  • 规模:

    • 2<=n<=1000,m<=6000
    • T<120

思路:1k点,6k边, 容易有环, tarjan缩点,然后就是一个DAG了(如果不联通, 肯定no), 然后分析得, 其实就是看能不能一条链可以把所有点穿起来, 如果有一个分叉, 处于两个分叉上的点就不能相互到达,但是这条链上可能有多出许多边, 干扰这条链, 问题就有点麻烦了。。

但是拓扑排序一下, 就完美的解决这个问题, 拓扑过程看是不是每一层都有一个入度为0的点, 也就是每一层是不是只有一个可以走的选择,如果每一层都是一个, 那么所有点走下来就是一条链了, 每次拓扑都把多余的边删掉了...太强了...


总结: 深刻理解拓扑, 拓扑可以判断是否可以一条链遍历所有点,可以在拓扑的过程中得出一些值, 比如万一让你找一个分叉,两个分叉的路径,也可以在拓扑路径中控制...考到有向图里的一些性质,一定想想拓扑排序

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 1e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, book[maxn][maxn];
int in[maxn], out[maxn];
vector<int> v[maxn], edge[maxn];
stack<int> s;
void init()
{
    memset(low, 0, sizeof(low));
    memset(id, 0, sizeof(id));
    memset(dfn, 0, sizeof(dfn));
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(book, 0, sizeof(book));
    scc_cnt = dfs_cnt = 0;
    for(int i = 0; i < maxn; i++)
        v[i].clear(), edge[i].clear();
    while(!s.empty())
        s.pop();
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++dfs_cnt;
    s.push(x);
    for(int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(!id[to])
            low[x] = min(low[x], dfn[to]);
    }
    if(low[x] == dfn[x])
    {
        scc_cnt++;
        while(1)
        {
            int u = s.top();
            s.pop();
            id[u] = scc_cnt;
            if(x == u) break;
        }
    }
}
void scc()
{
    for(int i = 1; i <= n ; i++)
        if(!dfn[i])
            tarjan(i);
}
void build()
{
  for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < v[i].size(); j++)
        {
            int to = v[i][j];
            if(id[i] != id[to] && !book[id[i]][id[to]])
            {
                edge[id[i]].push_back(id[to]);
                book[id[i]][id[to]] = 1;
                in[id[to]]++;
            }
        }
    }
}
int Top_sort()
{
    queue<int> q;
    for(int i = 1; i <= scc_cnt; i++)
        if(!in[i])
            q.push(i);
    while(!q.empty())
    {
        if(q.size() != 1)
            return 0;
        int u = q.front(); q.pop();
        for(int i = 0; i < edge[u].size(); i++)
        {
            int to = edge[u][i];
            in[to]--;
            if(in[to] == 0) q.push(to);
        }
    }
    return 1;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        init();
        scanf("%d%d", &n, &m);
        int x, y;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d", &x, &y);
            v[x].push_back(y);
        }
        scc();
        build();
        puts(Top_sort() ? "I love you my love and our love save us!" : "Light my fire!");
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值