ABC 256 E(图)

嘿嘿,我今天又来更一道E题!

E Takahashi's Anguish

题面

有n个人,每个人都要发一块糖,
但是他们之间有仇恨,第 $ i $ 个人仇恨第 $ X_i $ 个人。
如果第 $ X_i $ 个人发糖早于第 $ i $ 个人,那么第 $ i $ 个人将会有仇恨 $ C_i $ 点。
问怎样选择发糖的顺序,使得这些人的仇恨点数和最小。
你只需要输出最小仇恨点数和就行了。

思路

首先我们建图,对于每一个 $ i $,我们建一个 $ i \rightarrow X_i $ 的边(双向边,可以有重边),权值为 $ C_i $。
接着,我们找到图中的每一个环(详情见最后的 $ 0xFF $),计算这个环上最短的边,取所有环的总和。
实际写代码的时候,不需要记录权值,留着 $ C_i $ 即可。

代码

#include <iostream>
#include <string>
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iomanip>
#include <cstdlib>
#include <ctime>
#include <set>
#include <map>
#include <utility>
#include <queue>
#include <vector>
#include <bitset>
#include <stack>
#include <sstream>
#include <algorithm>

using namespace std;

#define maxn 200005

vector<int> gg[maxn];
queue<int> bfs_q;
int d[maxn];
long long c[maxn];

long long cycle_bfs(int x) {
	long long ans = 0x3f3f3f3f3f3f3f3f;
	bfs_q.push(x);
	while (bfs_q.size()) {
		int n = bfs_q.front();
		bfs_q.pop();
		ans = min(ans, c[n]);
		for (int adj_node : gg[n]) {
			d[adj_node]--;
			if (d[adj_node] == 1 && adj_node != x) {
				bfs_q.push(adj_node);
			}
		}
	}
	return ans;
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int x;
		scanf("%d", &x);
		gg[i].push_back(x - 1);
		gg[x - 1].push_back(i);
	}
	for (int i = 0; i < n; i++) {
		scanf("%lld", &c[i]);
	}
	for (int i = 0; i < n; i++) {
		d[i] = gg[i].size();
	}
	for (int i = 0; i < n; i++) {
		if (d[i] == 1) {
			bfs_q.push(i);
		}
	}
	while (bfs_q.size()) {
		int x = bfs_q.front();
		bfs_q.pop();
		for (int adj_node : gg[x]) {
			d[adj_node]--;
			if (d[adj_node] == 1) {
				bfs_q.push(adj_node);
			}
		}
	}
	long long ans = 0;
	for (int i = 0; i < n; i++) {
		if (d[i] == 2) {
			long long this_ans = cycle_bfs(i);
			ans += this_ans;
		}
	}
	printf("%lld", ans);
	return 0;
}

附 $ 0b11111111 $

我们先统计出每个点的度数。
然后从度数为一的开始BFS,遇到一个就把度数数组里讨厌的人度数减1。
如果他的度数为1了,那就把他压进队列。
之后再从度数为2的开始BFS找环。

posted @ 2022-06-20 21:36  A-Problem-Solver  阅读(40)  评论(0编辑  收藏  举报