传送门
Q市发生了一起特大盗窃案。这起盗窃案是由多名盗窃犯联合实施的,你要做的就是尽可能多的抓捕盗窃犯。
已知盗窃犯分布于N个地点,以及第i个地点初始有ai名盗窃犯。
特别的是,对于每一个地点,都有一个固定的地点v–当前如果某个盗窃犯位于地点,在下一个时刻他会移动到地点v。
你需要通过初始时在某些点设置哨卡来捉住他们。
现在你可以在M个地点设置哨卡,如果在某个地点设置哨卡,你可以抓获在任一时刻经过该地点的盗窃犯。
也就是说,哨卡存在的时间是无限长,但哨卡不能移动。
由题意可知最后形成的一定是若干个联通块,每一个联通块都可以看作是一棵树, 由find()函数可以找到每一棵树的根节点, 遍历树上的所有节点, 可以将全部节点的值累加到根节点,将各个联通块求得的值从大到小排序,取前m大。
复杂度O(nlogn)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<functional>
using namespace std;
const int maxn = 1e5 + 10;
long long int re[maxn];
int pre[maxn];
int val[maxn];
int find(int x)
{
if(pre[x] == x) return x;
return pre[x] = find(pre[x]);
}
void unite(int a, int b)
{
pre[find(a)] = find(b);
}
int main()
{
int n,m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
{
pre[i] = i;
scanf("%d", &val[i]);
}
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
unite(i, x);
}
int p = 0;
for(int i = 1; i <= n; i++)
re[find(i)] += val[i]; //各个联通块的值之和, 但这是分散的,
for(int i = 1; i <= n; i++)
if(re[i]) re[p++] = re[i];
sort(re, re + p, greater<long long>());
for(int i = 1; i <= min(p, m) -1; i++)
re[0] += re[i];
printf("%lld", re[0]);
}