题意:
现在有n个城市构成一张图(可能不连通),每个城市的亮度为bi,你每次可以选择一个大小为k的集合的城市,这个集合的城市不经过非集合的城市就能两两到达。并且将所有的亮度-1。你每次要选的k尽量大并且最终所有的城市的亮度为0.问你最少需要几次操作。
题解:
一开始的题意是有问题的?!!爷吐了
如果不是每次k要尽可能大,我感觉这道题有点难,至少我想了一段时间想不出来。后来题目改了之后这不就是水题吗就比较简单。
首先可以确定的是肯定是从亮度比较小的城市开始,将所有和他连通的城市的亮度减掉它的值,然后将这个点设为非联通,然后依次去做。但是很明显这是
的时间复杂度是不可行的,并且删边很麻烦,所以我们需要考虑加边的方法。
首先将所有点按值从大到小排序,然后依次加入并查集,然后当前的答案就加上(连通块的数量*(b[i]-b[i+1])),也就是说有x个连通块就要做x次b[i]-b[i+1]的操作。举个例子:
假设是这么一张图,首先加入一个3:
答案加上1*(3-3)=0
然后在加上一个3:
由于他们是不连通的,我们现在就有两个连通块
答案加上2*(3-2)=2
然后再加入2,我们现在有3个连通块
答案加上3*(2-1)=5
最后加入1,现在只剩下一个连通块,所以答案加上
1*(1-0)=6
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
vector<int>vec[N];
int fa[N];
int finds(int x){return x==fa[x]?fa[x]:fa[x]=finds(fa[x]);}
struct node{
int v,id;
bool operator< (const node& a)const {
return v>a.v;
}
}a[N];
int vis[N];
int main()
{
int t;
scanf("%d",&t);
srand(unsigned(time(NULL)));
while(t--){
int n,m,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
vec[i].clear(),fa[i]=i;
vis[i]=0;
scanf("%d",&a[i].v),a[i].id=i;
}
sort(a+1,a+1+n);
a[n+1]={0,0};
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),vec[x].push_back(y),vec[y].push_back(x);
int num=0;
ll ans=0;
for(int i=1;i<=n;i++){
int x=a[i].id;
num++;
vis[x]=1;
for(int j:vec[x]){
if(vis[j]){
int fax=finds(x);
int fay=finds(j);
if(fax==fay)continue;
num--;
int r=rand()%2;
if(r)
fa[fax]=fay;
else
fa[fay]=fax;
}
}
ans+=1ll*num*(a[i].v-a[i+1].v);
}
printf("%lld\n",ans);
}
return 0;
}