版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83184925
传送门
解析:
又是一道思维题。。。码量巨少。。。
其实拿到题目还想了想瓶颈路,但最后就是没有推出来最小生成树的结论。。。
好吧真是一道神题。。。
思路:
最优的方案一定是最小生成树构建过程中的某个图。将这个图中的边全部打通就是最优的方案。
其实证明还是有点巧妙。
首先如果我们已经求出了一个连通块需要被打通,那么打通这个连通块的代价是 ,这个十分//////
考虑这样一个图(懒得画了,毕竟画得奇丑)。
三个节点,两条边连接 和 ,权值 ,最小生成树构建中必然先连 再连接 ,因为直接连接 的时候占领这个连通块的代价是 ,而直接连接 的代价是 。
显然可以发现,先连权值更大的边是不够优的。
所以对于每一个连通块,我们维护打通这个连通块需要的至少的特种兵数量,然后我们直接在费用最少的地方集中放置,显然一定可以打通这个连通块。那么这个连通块的代价就是 ,这两个值可以分别在并查集上维护。
但是相对的,我们不一定要打通这个连通块,就算在构建最小生成树的时候在并查集上合并了两个连通块,但是在最终的方案中可能分别打通两个连通块更优,所以我们在维护打通当前连通块的费用时要单独维护。
然后就直接做一遍 ,顺便在合并的时候更新答案就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=100005,M=200005;
struct edge{
int u,v,w;
friend bool operator<(cs edge &a,cs edge &b){
return a.w<b.w;
}
}e[M];
int maxn[N],minn[N];
ll val[N];
ll tot;
ll ans;
int fa[N];
inline int getfa(int x){
while(x!=fa[x])x=fa[x]=fa[fa[x]];
return x;
}
int n,m;
signed main(){
n=getint();
m=getint();
for(int re i=1;i<=n;++i){
fa[i]=i;
maxn[i]=getint();
minn[i]=getint();
tot+=(val[i]=1ll*maxn[i]*minn[i]);
}
ans=tot;
for(int re i=1;i<=m;++i){
e[i].u=getint();
e[i].v=getint();
e[i].w=getint();
}
sort(e+1,e+m+1);
for(int re i=1;i<=m;++i){
int u=getfa(e[i].u),v=getfa(e[i].v);
if(u==v)continue;
fa[v]=u;
maxn[u]=max(maxn[u],max(maxn[v],e[i].w));
minn[u]=min(minn[u],minn[v]);
tot-=val[u]+val[v];
tot+=(val[u]=min(val[u]+val[v],1ll*minn[u]*maxn[u]));
ans=min(tot,ans);
}
cout<<ans;
return 0;
}