CF2275G Ranges 题解
我们把答案拆成好几个部分,并考虑分别求解。
-
拆除不影响连通性的边:因为拆除这些边只会带来收益且没有额外代价,我们尽可能拆除那些较大的边。
- 那么我们可以最开始跑一个最小生成树,用 Kruskal,如果过程中某条边没有被加入,就将其收益加进答案。
-
将所有连通块串起来:如果目前有
k 个连通块,则要先连k-1 条基础边。- 最开始最小生成树的时候不是维护了并查集嘛,直接根据这个并查集算出来有
k 个连通块,代价为(1+2+\dots+k) \times x 也就是\frac{k(k+1)}{2} \times x 。
- 最开始最小生成树的时候不是维护了并查集嘛,直接根据这个并查集算出来有
-
换旧买新:做完上面的操作已经完成了题目要求,但某些边可能过贵,可以选择卖掉后重新买入以赚取一定差价。
- 因为前面已经有了
k-1 条基础边需要增加,所以后续第i 条新边的代价为(k+i-1)x 。 - 为了最优,我们肯定尽可能选取那些贵的边卖出再买入,因此可以把最小生成树中的边的边权从大到小排序。
- 此时代价升序而收益降序,因此一定存在某个位置,使得前面的边是正收益,后面的边是负收益。这里的收益表示为当前边的边权减去重新买入的价格。
- 上述位置可以通过二分找出,那么只需将前面正收益的边带来的收益全部加上即可。
- 因为前面已经有了
然后就做完了。
::::success[code && submission]
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 2e5+5;
struct node{LL x,y,w;}ln[N];
int T,n,m,Q,fa[N];
bool vis[N];
LL tot,d[N],P,sum[N],Bas;
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
int FF(int x){return (fa[x]==x?x:fa[x]=FF(fa[x]));}
void init(){
for(int i=1;i<=n;i++)fa[i]=i;
return;
}
bool cmp(node d1,node d2){return d1.w<d2.w;}
bool cmp2(LL x,LL y){return x>y;}
LL Find(LL val){
LL l=1,r=tot,res=0;
while(l<=r){
LL mid=(l+r)>>1;
if(d[mid]>=(mid+Bas)*val)res=mid,l=mid+1;
else r=mid-1;
}
return res;
}
int main(){
T=read();
while(T--){
n=read(),m=read(),Q=read();
for(int i=1;i<=m;i++)
ln[i].x=read(),ln[i].y=read(),ln[i].w=read();
sort(ln+1,ln+m+1,cmp);
init();tot=0,P=0;
for(int i=1;i<=m;i++){
int x=ln[i].x,y=ln[i].y;
int fx=FF(x),fy=FF(y);
if(fx!=fy)fa[fx]=fy,d[++tot]=ln[i].w;
else P+=ln[i].w;
}
sort(d+1,d+tot+1,cmp2);
sum[0]=0;
for(int i=1;i<=tot;i++)sum[i]=sum[i-1]+d[i];
Bas=-1;
for(int i=1;i<=n;i++)vis[i]=0;
for(int i=1;i<=n;i++){
int fx=FF(i);
if(!vis[fx])vis[fx]=1,Bas++;
}
while(Q--){
LL val=read();
LL len=Find(val);
LL res=sum[len]-(Bas+1+Bas+len)*(len)/2*val;
res-=Bas*(Bas+1)/2*val;
res+=P;
cout<<res<<" ";
}
cout<<"\n";
}
return 0;
}
::::
如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!