bzoj 3206: [Apio2013]道路费用 最小生成树

       注意到k很小,那么我们把这给定的k条边设为-inf跑最小生成树,此时最小生成树中非这k条边的边是一定要加入最终的最小生成树的,那么可以将点缩在一起,这样就只剩下k+1个点了。然后对原图中的点跑最小生成树(缩点后的最小生成树),此时最小生成树中的边是可能要选的。那么就只剩下k条边了。

       然后就可以2^k枚举要选的边的子集,注意要选的边不能构成环。然后就得到了一颗树,随便乱搞一下就好可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 300005
#define ll long long
using namespace std;

int n,m,cnt,tot,tp,rt,bin[35],c[35],fa[2][N],f[N],d[N],fst[N],pnt[105],nxt[105]; ll sz[N],val[N];
struct node{ int x,y,z; }a[M],b[35];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
bool cmp(node u,node v){ return u.z<v.z; }
int getfa(int k,int x){ return (fa[k][x]==x)?x:fa[k][x]=getfa(k,fa[k][x]); }
void dfs(int x){
	sz[x]=val[x]; int p;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[1][x]){
			fa[1][y]=x; d[y]=d[x]+1; dfs(y);
			sz[x]+=sz[y];
		}
	}
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
int main(){
	n=read(); m=read(); cnt=read(); int i;
	for (i=1; i<=m; i++){
		a[i].x=read(); a[i].y=read(); a[i].z=read();
	}
	sort(a+1,a+m+1,cmp);
	for (i=1; i<=n; i++) fa[0][i]=fa[1][i]=i;
	for (i=1; i<=cnt; i++){
		b[i].x=read(); b[i].y=read();
		int u=getfa(0,b[i].x),v=getfa(0,b[i].y);
		if (u!=v) fa[0][u]=v;
	}
	for (i=1; i<=m; i++){
		int u=getfa(0,a[i].x),v=getfa(0,a[i].y);
		if (u!=v){ fa[0][u]=v; fa[1][getfa(1,a[i].x)]=getfa(1,a[i].y); }
	}
	rt=getfa(1,1);
	for (i=1; i<=n; i++) val[getfa(1,i)]+=read();
	for (i=1; i<=n; i++) if (getfa(1,i)==i) c[++c[0]]=i;
	for (i=1; i<=cnt; i++){
		b[i].x=getfa(1,b[i].x); b[i].y=getfa(1,b[i].y);
	}
	for (i=1; i<=m; i++){
		a[i].x=getfa(1,a[i].x); a[i].y=getfa(1,a[i].y);
	}
	for (i=1; i<=m; i++){
		int u=getfa(1,a[i].x),v=getfa(1,a[i].y);
		if (u!=v){ fa[1][u]=v; a[++tp]=a[i]; }
	}
	ll ans=0; int k;
	bin[0]=1; for (i=1; i<=cnt; i++) bin[i]=bin[i-1]<<1;
	for (k=0; k<bin[cnt]; k++){
		tot=0;
		for (i=1; i<=c[0]; i++){
			int x=c[i]; fst[x]=fa[1][x]=0;
			fa[0][x]=x; f[x]=1000000000;
		}
		for (i=1; i<=cnt; i++)
			if (k&bin[i-1]){
				int u=getfa(0,b[i].x),v=getfa(0,b[i].y);
				if (u==v) break; fa[0][u]=v;
				add(b[i].x,b[i].y); add(b[i].y,b[i].x);
			}
		if (i<=cnt) continue;
		for (i=1; i<=cnt; i++){
			int u=getfa(0,a[i].x),v=getfa(0,a[i].y);
			if (u!=v){ fa[0][u]=v; add(a[i].x,a[i].y); add(a[i].y,a[i].x); }
		}
		dfs(rt);
		for (i=1; i<=cnt; i++){
			int u=a[i].x,v=a[i].y;
			if (d[u]<d[v]) swap(u,v);
			for (; d[u]!=d[v]; u=fa[1][u]) f[u]=min(f[u],a[i].z);
			for (; u!=v; u=fa[1][u],v=fa[1][v]){
				f[u]=min(f[u],a[i].z); f[v]=min(f[v],a[i].z);
			}
		}
		ll tmp=0;
		for (i=1; i<=cnt; i++) if (k&bin[i-1]){
			int u=b[i].x,v=b[i].y;
			if (d[u]<d[v]) swap(u,v); tmp+=sz[u]*f[u];
		}
		ans=max(ans,tmp);
	}
	printf("%lld\n",ans);
	return 0;
}


by lych

2016.4.6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值