又是一道思维题。。。码量巨少。。。
其实拿到题目还想了想瓶颈路,但最后就是没有推出来最小生成树的结论。。。
好吧真是一道神题。。。
最优的方案一定是最小生成树构建过程中的某个图。将这个图中的边全部打通就是最优的方案。
其实证明还是有点巧妙。
首先如果我们已经求出了一个连通块需要被打通,那么打通这个连通块的代价是 m a x { m a x { a } , m a x { c } } × m i n { b } max\{max\{a\},max\{c\}\}\times min\{b\} max{max{a},max{c}}×min{b},这个十分//
考虑这样一个图(懒得画了,毕竟画得奇丑)。
三个节点,两条边连接 < 1 , 2 > <1,2> <1,2>和 < 2 , 3 > <2,3> <2,3>,权值 c 1 > c 2 c_1>c_2 c1>c2,最小生成树构建中必然先连 c 2 c_2 c2再连接 c 1 c_1 c1,因为直接连接 c 1 c_1 c1的时候占领这个连通块的代价是 m a x { a 1 , a 2 , c 1 } × m i n { b 1 , b 2 } + b 3 × a 3 max\{a_1,a_2,c_1\}\times min\{b_1,b_2\}+b_3\times a_3 max{a1,a2,c1}×min{b1,b2}+b3×a3,而直接连接 c 2 c_2 c2的代价是 m a x { a 2 , a 3 , c 2 } × m i n { b 2 , b 3 } + a 1 × b 1 max\{a_2,a_3,c_2\}\times min\{b_2,b_3\}+a_1\times b_1 max{a2,a3,c2}×min{b2,b3}+a1×b1。
显然可以发现,先连权值更大的边是不够优的。
所以对于每一个连通块,我们维护打通这个连通块需要的至少的特种兵数量,然后我们直接在费用最少的地方集中放置,显然一定可以打通这个连通块。那么这个连通块的代价就是 m a x { a } × m i n { b } max\{a\}\times min\{b\} max{a}×min{b},这两个值可以分别在并查集上维护。
但是相对的,我们不一定要打通这个连通块,就算在构建最小生成树的时候在并查集上合并了两个连通块,但是在最终的方案中可能分别打通两个连通块更优,所以我们在维护打通当前连通块的费用时要单独维护。
然后就直接做一遍 K r u s k a l Kruskal Kruskal,顺便在合并的时候更新答案就行了。
代码:
#include
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;
}