Hdu 6763 Total Eclipse 并查集+贪心
题目
题意
刚开始有一个联通的无向图,每个点都有自己的亮度,可以选择连通块,每次操作对连通块里面的点的亮度都进行减1,问最少要操作多少次才能都减为0.
(如果两点之间相隔着的点的亮度为0,视为这两点不连通)
题解
首先让所有点都不连通。用vector<> g记录连通情况。
我们先把所有权值加起来计算sum,然后给每个点按照权值从大到小排序,如果权值小的点和权值大的点应该是在一个连通块上,还没有连通起来的话,sum -= val,然后再将他们连通起来起来。最后就可以得出正确答案了。(PS:并查集需要使用路径压缩,不然会TLE)
爆破冠军。。。
AC代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 2147483647
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
struct node
{
ll v;
int id;
} a[maxn];
vector<int> v[maxn];
int vis[maxn],pre[maxn];
int find(int x)
{
if(x == pre[x]) return x;
return pre[x] = find(pre[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)
{
pre[fx] = fy;
}
}
bool cmp(node x,node y)
{
return x.v>y.v;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
ll sum=0;
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%lld",&a[i].v);
a[i].id=i;
sum+=a[i].v;
vis[i]=0;
pre[i]=i;
v[i].clear();
}
sort(a+1,a+n+1,cmp);
while(m--)
{
int x,y;
scanf("%d %d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
vis[a[1].id]=1;
for(int i=2; i<=n; i++)
{
int id = a[i].id;
int size = v[id].size();
for(int j=0; j<size; j++)
{
int x = v[id][j];
int fa,fb;
if(vis[x]==1)
{
fa = find(x),fb = find(id);
if(fa!=fb)
{
sum -= a[i].v;
pre[fa] = fb;
}
}
}
vis[id]=1;
}
printf("%lld\n",sum);
}
return 0;
}