2018.10.19【BZOJ4973】比特战争(最小生成树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83184925

传送门


解析:

又是一道思维题。。。码量巨少。。。

其实拿到题目还想了想瓶颈路,但最后就是没有推出来最小生成树的结论。。。

好吧真是一道神题。。。

思路:

最优的方案一定是最小生成树构建过程中的某个图。将这个图中的边全部打通就是最优的方案。

其实证明还是有点巧妙。

首先如果我们已经求出了一个连通块需要被打通,那么打通这个连通块的代价是 m a x { m a x { a } , m a x { c } } × m i n { b } max\{max\{a\},max\{c\}\}\times min\{b\} ,这个十分//////

考虑这样一个图(懒得画了,毕竟画得奇丑)。

三个节点,两条边连接 < 1 , 2 > <1,2> < 2 , 3 > <2,3> ,权值 c 1 > c 2 c_1>c_2 ,最小生成树构建中必然先连 c 2 c_2 再连接 c 1 c_1 ,因为直接连接 c 1 c_1 的时候占领这个连通块的代价是 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 ,而直接连接 c 2 c_2 的代价是 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

显然可以发现,先连权值更大的边是不够优的。

所以对于每一个连通块,我们维护打通这个连通块需要的至少的特种兵数量,然后我们直接在费用最少的地方集中放置,显然一定可以打通这个连通块。那么这个连通块的代价就是 m a x { a } × m i n { b } max\{a\}\times min\{b\} ,这两个值可以分别在并查集上维护。

但是相对的,我们不一定要打通这个连通块,就算在构建最小生成树的时候在并查集上合并了两个连通块,但是在最终的方案中可能分别打通两个连通块更优,所以我们在维护打通当前连通块的费用时要单独维护。

然后就直接做一遍 K r u s k a l Kruskal ,顺便在合并的时候更新答案就行了。


代码:

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

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/83184925