多校第二场——1001 Total Eclipse

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);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43330910/article/details/107616064