Problem Description
There are n cities and m bidirectional roads in Byteland. These cities are labeled by 1,2,…,n, the brightness of the i-th city is bi.
Magician Sunset wants to play a joke on Byteland by making a total eclipse such that the brightness of every city becomes zero. Sunset can do the following operations for arbitrary number of times:
· Select an integer k (1≤k≤n).
· Select k distinct cities c1,c2,…,ck (1≤ci≤n) such that they are connected with each other. In other words, for every pair of distinct selected cities ci and cj (1≤i<j≤k), if you are at city ci, you can reach city cj without visiting cities not in {c1,c2,…,ck}.
· For every selected city ci (1≤i≤k), decrease bci by 1.
Note that Sunset will always choose k with the maximum possible value. Now Sunset is wondering what is the minimum number of operations he needs to do, please write a program to help him.
Input
The first line of the input contains a single integer T (1≤T≤10), the number of test cases.
For each case, the first line of the input contains two integers n and m (1≤n≤100000, 1≤m≤200000), denoting the number of cities and the number of roads.
The second line of the input contains n integers b1,b2,…,bn (1≤bi≤109), denoting the brightness of each city.
Each of the following m lines contains two integers ui and vi (1≤ui,vi≤n,ui≠vi), denoting an bidirectional road between the ui-th city and the vi-th city. Note that there may be multiple roads between the same pair of cities.
Output
For each test case, output a single line containing an integer, the minimum number of operations.
Sample Input
1
3 2
3 2 3
1 2
2 3
Sample Output
4
题意:给一个无向图,n个点,m条边,每个点有有一个点权w,我们可以选择k个互相连通的的点进行减1,问当所有点为0时,操作的最小次数。
思路:我当时遍历用的宽搜,结果时间没爆,内存爆了,???,后面就一直在想这个图的存储的数据结构,就想歪了。应该用并查集缩短查询时间。
题解:每次一定是选择一个极大连通块,将里面所有数同时减小,直到最小值变成 0,然后将变 成 0 的点删除,分裂成多个连通块再接着做。 为了实现这个策略,可以将整个过程倒过来看,变成按照 b 的值从大到小依次加入每个点。 加入每个点 x 时遍历与 x 相连的所有边 (x,y),如果 y 在 x 之前加入且 x 和 y 不连通则将 x 和 y 合并,并将 y 所在连通块的树根的父亲设为 x,得到一棵有根树。那么每个点 x 在成为最小值之前已经被做了 bfatherx 次操作,所以每个点 x 对答案的贡献为 bx −bfatherx。 使用并查集支持路径压缩,时间复杂度 O((n + m)logn)。
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 100010;
const int M = 200010;
int end;
int num[N], num1[N], num2[N], f1[N], f2[N], w[N];
int v[M / 2], next[M / 2];
bool cmp(int x, int y)
{
return num[x] > num[y];
}
int find(int x)
{
return f1[x] == x ? x : f1[x] = find(f1[x]);
}
void uni(int x, int y)
{
v[++end] = y;
next[end] = num2[x];
num2[x] = end;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n;
int m;
scanf("%d%d", &n, &m);
end = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &num[i]);
num1[i] = f1[i] = i;
num2[i] = f2[i] = w[i] = 0;
}
int x;
int y;
while (m--)
{
scanf("%d%d", &x, &y);
uni(x, y);
uni(y, x);
}
sort(num1 + 1, num1 + n + 1, cmp);
for (int i = 1; i <= n; i++)
{
x = num1[i];
w[x] = 1;
for (int j = num2[x]; j; j = next[j])
{
y = v[j];
if (!w[y])
{
continue;
}
y = find(y);
if (y == x)
{
continue;
}
f2[y] = f1[y] = x;
}
}
long long ans = 0;
for (int i = 1; i <= n; i++)
{
ans += (num[i] - num[f2[i]]);
}
printf("%lld\n", ans);
}
}