51 nod 1456 小K的技术(强连通 + 并查集)

1456 小K的技术

题目来源: CodeForces

基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题

 

苏塞克王国是世界上创新技术的领先国家,在王国中有n个城市,标记为1到n。

由于小K的研究,我们最终能过在两个城市之间建立传输管道,一个传输管道能单向连接两个城市,即,一个从城市x到城市y的传输管道不能被用于从城市y传输到城市x。在每个城市之间的运输系统已经建立完善,因此,如果从城市x到城市y的管道和从城市y到城市z的管道都被已经被建立,人们能够立即从x到z。

小K也研究了国家政治,他认为在这m对城市(ai, bi) (1 ≤ i ≤ m)之间的传输尤其重要。他正在计划为每个重要城市对(ai, bi)建立传输管道,通过使用一个或多个传输管道,我们可以从城市ai到城市bi(但不需要从城市bi到城市ai)。我们要找出必须建立的传输管道的最小数。至今,还没有传输管道被建立,在每个城市之间也没有其他有效的传输方式。

 

对于第一个样例,其中一条最优路径如下图:

Input
第一行是两个以空格隔开的整数n和m(2 ≤ n ≤10^5 , 1 ≤ m ≤ 10^5 ),分别表示在苏塞克王国中的城市数和重要城市对的数。
之后m行描述重要城市对,第i行 (1 ≤ i ≤ m)包含两个以空格隔开的整数ai和bi(1 ≤ ai, bi ≤ n, ai ≠ bi),表示必须能通过一条或两条传输管道从城市ai到城市bi(但不需要从城市bi到城市ai),我们保证所有的城市对(ai, bi)是唯一的。
Output
输出满足小K目的所需要的传输管道的最小数。
Input示例
4 5
1 2
1 3
1 4
2 3
2 4
Output示例
3

 

/*
51 nod 1456 小K的技术(强连通 + 并查集)

problem:
给你n个城市和m次操作. 每次a,b表示两个城市a->b之间应该连通. 而添加的管道是单向的,求最少需要多少的
管道才能满足要求.

solve:
在一个联通块里面,可以发现最糟糕的情况就是没有环,那么n-1条就能够满足条件.
但是如果存在环,这样的话n个点需要围成一个圈,所以需要n条管道.

所以可以用tarjan算法来判断一下强连通就好了,只要存在强连通分支,则说明当前联通块需要n个管道.
然后用并查集来记录每个联通块中点的数量.
- - 一直WR. 结果发现如果联通块a和联通块b被一个单向边连接. 我在搜索a的时候并不能到达b,判断出现了一些问题,
这里注意一下就好了

hhh-2016/09/08-21:11:22
*/
#pragma comment(linker,"/STACK:124000000,124000000")
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <math.h>
#include <queue>
#include <set>
#include <map>
#define lson  i<<1
#define rson  i<<1|1
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
#define scanfi(a) scanf("%d",&a)
#define scanfs(a) scanf("%s",a)
#define scanfl(a) scanf("%I64d",&a)
#define scanfd(a) scanf("%lf",&a)
#define key_val ch[ch[root][1]][0]
#define eps 1e-7
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const ll mod = 1000000007;
const int maxn = 300050;
const double PI = acos(-1.0);
const int limit = 33;
int pre[maxn];
int cnt[maxn];
template<class T> void read(T&num)
{
    char CH;
    bool F=false;
    for(CH=getchar(); CH<'0'||CH>'9'; F= CH=='-',CH=getchar());
    for(num=0; CH>='0'&&CH<='9'; num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
}
int stk[70], tp;
template<class T> inline void print(T p)
{
    if(!p)
    {
        puts("0");
        return;
    }
    while(p) stk[++ tp] = p%10, p/=10;
    while(tp) putchar(stk[tp--] + '0');
    putchar('\n');
}

int fin(int x)
{
    if(pre[x] == x) return x;
    return pre[x] = fin(pre[x]);
}

void unio(int a,int b)
{
    int ta= fin(a);
    int tb = fin(b);
    if(ta == tb)
        return ;
    pre[tb] = ta;
    cnt[ta] += cnt[tb];
}

int tot = 0;
int index,top,scc;
int head[maxn];
int low[maxn],dfn[maxn],Stack[maxn],Belong[maxn],num[maxn];
bool Instack[maxn];
ll ans[maxn];
struct node
{
    int u,v,nex;
} edge[maxn];

void add_edge(int u,int v)
{
    edge[tot].u = u,edge[tot].v = v,edge[tot].nex = head[u],head[u] = tot++;
}

void init()
{
    tot = 0;
    for(int i = 1; i <= maxn-50; i++)
    {
        ans[i] = 0;
        head[i] = -1,pre[i] = i;
        num[i] = dfn[i] = 0;
        Instack[i] = false;
        cnt[i] = 1;
    }
    index = top = scc = 0;
}
int flag;
//ll cnt;
void Tarjan(int u)
{
    int v;
//    if(!dfn[u])
//        cnt ++ ;
    low[u] = dfn[u] = ++index;
    Stack[top++] = u;
    Instack[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].nex)
    {
        v = edge[i].v;
        if(! dfn[v])
        {
            Tarjan(v);
            if(low[u] > low[v])
                low[u] = low[v];
        }
        else if(Instack[v] && low[u] > dfn[v])
            low[u] = dfn[v];
    }
    if(low[u] == dfn[u])
    {
        scc ++ ;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = scc;
            num[scc] ++ ;
            if(num[scc] > 1)
                flag = 1;
        }
        while(v != u);
    }
}


int main()
{
//    freopen("in.txt","r",stdin);
    int n,m;
    int u,v;

    while(scanfi(n) != EOF)
    {
        scanfi(m);
        init();
        for(int i = 1; i <= m; i++)
        {
            read(u),read(v);
            add_edge(u,v);
            unio(u,v);
        }
        for(int i = 1; i <= n; i++)
        {
            if(!dfn[i])
            {
                flag = 0;
                Tarjan(i);
                int anc = fin(i);
                if(ans[anc] == cnt[anc])
                    continue;
                if(flag)
                    ans[anc] = cnt[anc];
                else
                    ans[anc] = cnt[anc]-1;
            }
        }
        ll toans = 0;
        for(int i = 1;i <= n;i++)
            toans += ans[i];
        printf("%I64d\n",toans);
    }
    return 0;
}
/*
4 6
1 2
1 4
2 3
2 4
3 2
3 4



*/

  

转载于:https://www.cnblogs.com/Przz/p/5854558.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值