51nod 1456:小K的技术

苏塞克王国依赖小K的技术建立单向传输管道,以连接n个城市。小K关注m对重要城市之间的传输,并寻求最小化所需管道数量。问题转化为寻找最优路径,若城市弱联通则需要n-1条边,强联通则形成环需n条边。解决方案涉及并查集算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源:  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

将原图缩点,如果n个点是弱联通不是强连通,那么将是n-1条边。如果是强连通,那么就是形成环,n条边。

然后将不同的块连接起来,并查集。

代码:

#pragma warning(disable:4996)  
#include <iostream>  
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>  
#include <string>  
#include <cmath>
#include <queue>
#include <map>
using namespace std;

typedef long long ll;
#define INF 0x3fffffff
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;

int n, m, edge_num, Dindex, Stop, Bcnt;
int head[maxn], LOW[maxn], DFN[maxn], instack[maxn], Stack[maxn], Belong[maxn], num[maxn], mar[maxn], fa[maxn];

struct edge {
	int to;
	int next;
}Edge[maxn];

void init()
{
	edge_num = 0;
	Stop = Bcnt = Dindex = 0;

	memset(Edge, -1, sizeof(Edge));
	memset(head, -1, sizeof(head));
	memset(LOW, 0, sizeof(LOW));
	memset(DFN, 0, sizeof(DFN));
	memset(instack, 0, sizeof(instack));
	memset(Stack, 0, sizeof(Stack));
	memset(Belong, 0, sizeof(Belong));
}

void addedge(int u, int v)
{
	Edge[edge_num].to = v;
	Edge[edge_num].next = head[u];
	head[u] = edge_num;
	edge_num++;
}

void tarjan(int i)
{
	int j;
	DFN[i] = LOW[i] = ++Dindex;
	instack[i] = true;
	Stack[++Stop] = i;

	for (j = head[i]; j != -1; j = Edge[j].next)
	{
		int v = Edge[j].to;
		if (DFN[v] == 0)
		{
			tarjan(v);
			LOW[i] = min(LOW[i], LOW[v]);
		}
		else if (instack[v] == 1)
		{
			LOW[i] = min(LOW[i], DFN[v]);
		}
	}

	if (DFN[i] == LOW[i])
	{
		Bcnt++;
		do
		{
			j = Stack[Stop--];
			instack[j] = false;
			Belong[j] = Bcnt;
			num[Bcnt]++;
			if (num[Bcnt] != 1)
			{
				mar[Bcnt] = 1;
			}
			else
			{
				mar[Bcnt] = 0;
			}
		} while (j != i);
	}
}

void input()
{
	int i, u, v;

	scanf("%d%d", &n, &m);
	for (i = 1; i <= m; i++)
	{
		scanf("%d%d", &u, &v);
		addedge(u, v);
	}
}

int find(int x)
{
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void cal()
{
	int i, j;
	for (i = 1; i <= Bcnt; i++)
	{
		fa[i] = i;
	}

	for (i = 1; i <= n; i++)
	{
		for (j = head[i]; j != -1; j = Edge[j].next)
		{
			int s = Belong[i];
			int e = Belong[Edge[j].to];
			int fs = find(s);
			int fe = find(e);
			if (fs != fe)
			{
				fa[fs] = fe;
				num[fe] += num[fs];
				mar[fe] += mar[fs];
			}
		}
	}
}

void solve()
{
	int i;
	for (i = 1; i <= n; i++)
	{
		if (!DFN[i])
			tarjan(i);
	}
	cal();

	ll res = 0;
	for (i = 1; i <= Bcnt; i++)
	{
		if (fa[i] == i)
		{
			if (mar[i] == 0)
			{
				res += (num[i] - 1);
			}
			else
			{
				res += num[i];
			}
		}
	}
	printf("%I64d", res);
}
int main() 
{
	//freopen("i.txt", "r", stdin);
	//freopen("o.txt", "w", stdout);

	init();
	input();
	solve();

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值