CF1027D Mouse Hunt 题解

先容我吐槽一句:话说老鼠竟然变成了可怕的怪物。。。。。。有那么可怕吗?好吧还真有

开始正题。

显然这是一道图论题。

我们将 n n n 个房间视为 n n n 个点,将 ( i , a i ) (i,a_i) (i,ai) 连一条边。

那么问题就变成了:在一张有向图上,每个点都有权值 c i c_i ci,选取一些点使得所有点沿边行动过程中一定会碰上这些点,求最小权值和。

这不是就变成了求环上最小权值吗?

为什么?

首先对于一个环(包括自环),如果环上有一个点被选中,那么其余点都能到这个点。

那么那些不在环上的呢?由于每一个点恰好有一条出边,那么不在环上的点总能到达在环上的点。

于是问题解决。我们把环找出来,然后取出权值最小的点即可。

这里有两种思路:

  1. 基环树森林。但是我不会啊
  2. 拓扑排序+并查集。

大致思路就是先使用拓扑排序删除不在环上的点,剩下的点合并,取出最小值,求和。

代码:

#include <bits/stdc++.h>
#define Min(a, b) ((a < b) ? a : b)
using namespace std;

typedef long long LL;
const int MAXN = 2e5 + 10;
int n, c[MAXN], a[MAXN], cnt[MAXN], fa[MAXN];
LL ans, v[MAXN];
bool book[MAXN];

int read()
{
    
    
	int sum = 0, fh = 1; char ch = getchar();
	while (ch < '0' || ch > '9') {
    
    if (ch == '-') fh = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {
    
    sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
	return sum * fh;
}

int gf(int x) {
    
    return (fa[x] == x) ? x : (fa[x] = gf(fa[x]));}
void hb(int x, int y) {
    
    if (gf(x) != gf(y)) fa[fa[x]] = fa[y];}

void topsort()
{
    
    
	queue <int> q;
	for (int i = 1; i <= n; ++i)
		if (cnt[i] == 0) {
    
    book[i] = 1; q.push(i);}
	while (!q.empty())
	{
    
    
		int x = q.front();
		q.pop();
		cnt[a[x]]--;
		if (cnt[a[x]] == 0) {
    
    book[a[x]] = 1; q.push(a[x]);}
	}
}

int main()
{
    
    
	n = read();
	for (int i = 1; i <= n; ++i) c[i] = read();
	for (int i = 1; i <= n; ++i) {
    
    cnt[a[i] = read()]++;}
	topsort();
	for (int i = 1; i <= n; ++i) fa[i] = i;
	for (int i = 1; i <= n; ++i)
		if (!book[i] && !book[a[i]]) hb(i, a[i]);
	for (int i = 1; i <= n; ++i) fa[i] = gf(i);
	for (int i = 1; i <= n; ++i) v[i] = 0x7f7f7f7f;
	for (int i = 1; i <= n; ++i) v[fa[i]] = Min(v[fa[i]], c[i]);
	for (int i = 1; i <= n; ++i)
		if (fa[i] == i && !book[i]) ans += (LL)v[i];
	printf("%lld\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/BWzhuzehao/article/details/113756104