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找环。