2018.10.19【BZOJ4973】位元戰爭(最小生成樹)
阿新 • • 發佈:2018-12-16
傳送門
解析:
又是一道思維題。。。碼量巨少。。。
其實拿到題目還想了想瓶頸路,但最後就是沒有推出來最小生成樹的結論。。。
好吧真是一道神題。。。
思路:
最優的方案一定是最小生成樹構建過程中的某個圖。將這個圖中的邊全部打通就是最優的方案。
其實證明還是有點巧妙。
首先如果我們已經求出了一個連通塊需要被打通,那麼打通這個連通塊的代價是,這個十分//////
考慮這樣一個圖(懶得畫了,畢竟畫得奇醜)。
三個節點,兩條邊連線和,權值,最小生成樹構建中必然先連再連線,因為直接連線的時候佔領這個連通塊的代價是,而直接連線的代價是。
顯然可以發現,先連權值更大的邊是不夠優的。
所以對於每一個連通塊,我們維護打通這個連通塊需要的至少的特種兵數量,然後我們直接在費用最少的地方集中放置,顯然一定可以打通這個連通塊。那麼這個連通塊的代價就是,這兩個值可以分別在並查集上維護。
但是相對的,我們不一定要打通這個連通塊,就算在構建最小生成樹的時候在並查集上合併了兩個連通塊,但是在最終的方案中可能分別打通兩個連通塊更優,所以我們在維護打通當前連通塊的費用時要單獨維護。
然後就直接做一遍,順便在合併的時候更新答案就行了。
程式碼:
#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;
}