1. 程式人生 > >2018.10.19【BZOJ4973】位元戰爭(最小生成樹)

2018.10.19【BZOJ4973】位元戰爭(最小生成樹)

傳送門

解析:

又是一道思維題。。。碼量巨少。。。

其實拿到題目還想了想瓶頸路,但最後就是沒有推出來最小生成樹的結論。。。

好吧真是一道神題。。。

思路:

最優的方案一定是最小生成樹構建過程中的某個圖。將這個圖中的邊全部打通就是最優的方案。

其實證明還是有點巧妙。

首先如果我們已經求出了一個連通塊需要被打通,那麼打通這個連通塊的代價是max{max{a},max{c}}×min{b}max\{max\{a\},max\{c\}\}\times min\{b\},這個十分//////

考慮這樣一個圖(懶得畫了,畢竟畫得奇醜)。

三個節點,兩條邊連線<

1,2><1,2><2,3><2,3>,權值c1>c2c_1>c_2,最小生成樹構建中必然先連c2c_2再連線c1c_1,因為直接連線c1c_1的時候佔領這個連通塊的代價是max{a1,a2,c1}×min{b1,b2}+b3×a3max\{a_1,a_2,c_1\}\times min\{b_1,b_2\}+b_3\times a_3,而直接連線c2c_2的代價是max{a2,a3,c2}×min{b2,b3}+a1×b1max\{a_2,a_3,c_2\}\times min\{b_2,b_3\}+a_1\times b_1

顯然可以發現,先連權值更大的邊是不夠優的。

所以對於每一個連通塊,我們維護打通這個連通塊需要的至少的特種兵數量,然後我們直接在費用最少的地方集中放置,顯然一定可以打通這個連通塊。那麼這個連通塊的代價就是max{a}×min{b}max\{a\}\times min\{b\}

},這兩個值可以分別在並查集上維護。

但是相對的,我們不一定要打通這個連通塊,就算在構建最小生成樹的時候在並查集上合併了兩個連通塊,但是在最終的方案中可能分別打通兩個連通塊更優,所以我們在維護打通當前連通塊的費用時要單獨維護。

然後就直接做一遍KruskalKruskal,順便在合併的時候更新答案就行了。

程式碼:

#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;
}