• [bzoj3206][Apio2013]道路费用


    Description

    幸福国度可以用 N 个城镇(用 1 到 N 编号)构成的集合来描述,这些城镇最开始由 M 条双向道路(用 1 到 M 编号)连接。城镇 1 是中央城镇。保证一个人从城镇 1 出发,经过这些道路,可以到达其他的任何一个城市。这些道路都是收费道路,道路 i 的使用者必须向道路的主人支付 \(c_i\) 分钱的费用。已知所有的这些 \(c_i\) 是互不相等的。最近有 K 条新道路建成,这些道路都属于亿万富豪 Mr. Greedy。

    Mr. Greedy 可以决定每条新道路的费用(费用可以相同),并且他必须在明天宣布这些费用。

    两周以后,幸福国度将举办一个盛况空前的嘉年华!大量的参与者将沿着这些道路游行并前往中央城镇。共计 \(p_j\) 个参与者将从城镇 j 出发前往中央城镇。这些人只会沿着一个选出的道路集合前行,并且这些选出的道路将在这件事的前一天公布。根据一个古老的习俗,这些道路将由幸福国度中最有钱的人选出,也就是 Mr. Greedy。同样根据这个习俗,Mr. Greedy 选出的这个道路集合必须使所有选出道路的费用之和最小,并且仍要保证任何人可以从城镇 j 前往城镇 1 (因此,这些选出的道路来自将费用作为相应边边权的 “最小生成树”)。如果有多个这样的道路集合,Mr. Greedy 可以选其中的任何一个,只要满足费用和是最小的。

    Mr. Greedy 很明确地知道,他从 K 条新道路中获得的收入不只是与费用有关。一条道路的收入等于所有经过这条路的人的花费之和。更准确地讲,如果 p 个人经过道路 i,道路 i 产生的收入为乘积 \(c_i\times p\)。注意 Mr. Greedy 只能从新道路收取费用,因为原来的道路都不属于他。

    Mr. Greedy 有一个阴谋。他计划通过操纵费用和道路的选择来最大化他的收入。他希望指定每条新道路的费用(将在明天公布),并且选择嘉年华用的道路(将在嘉年华的前一天公布),使得他在 K 条新道路的收入最大。注意 Mr. Greedy 仍然需要遵循选出花费之和最小的道路集合的习俗。

    你是一个记者,你想揭露他的计划。为了做成这件事,你必须先写一个程序来确定 Mr. Greedy 可以通过他的阴谋获取多少收入。

    Input

    第一行包含三个由空格隔开的正整数 N,M 和 K。

    接下来的 M 行描述最开始的 M 条道路。这 M 行中的第 i 行包含由空格隔开的整数 \(a_i,b_i,c_i\),表示有一条在 aiai 和 \(b_i\) 之间,费用为 \(c_i\) 的双向道路。保证 \(1≤a_i,b_i≤N\)。保证如果 i≠i′,则 \(c_i≠c_i′\)

    接下来的 K 行描述新建的 K 条道路。这 K 行中的第 i 行包含由空格隔开的整数 \(x_i\)\(y_i\),表示有一条连接城镇 \(x_i\)\(y_i\) 新道路。保证 \(1≤x_i,y_i≤n\)

    最后一行包含 N 个由空格隔开的整数,其中的第 j 个为 \(p_j\),表示从城镇 j 前往城镇 1 的人数。

    保证在任意两个城市之间,最多只有一条道路(包括新建的道路)。

    Output

    你的程序必须输出恰好一个整数到标准输出,表示能获得的最大的收入。

    Sample Input

    5 5 1
    3 5 2
    1 2 3
    2 3 5
    2 4 4
    4 3 6
    1 3
    10 20 30 40 50
    

    Sample Output

    400
    

    HINT

    N≤100000,M≤300000,K≤20。

    Solution

    显然要尽量让 k 条边在最小生成树上才能使答案尽量优.
    然后尽量让 k 条边在最小生成树上,求个最小生成树,因为权值互不相同,所以最小生成树上的非 k 边一定在最终的最小生成树上,按这些边把联通块缩成点.
    此时只有最多 k 个点.
    然后不用 k 条边,求出缩点后的最小生成树,那么此时的最小生成树上的边是备选边.
    在合法的情况下,枚举这 k 条边选或不选,然后求出此时的最小生成树和所有备选边对此时树结构的路径长度的限制:一条备选边只会影响一条k边,所以直接记录一个点到其父亲那条边的上限即可.
    答案为所有情况的价值的最大值.

    #define K 25
    #define N 100005
    #define M 300005
    #define INF 1000001
    typedef long long ll;
    struct edge{
    	int l,r,w;
    	bool friend operator < (edge a,edge b){
    		return a.w<b.w;
    	}
    }a[M],b[N];
    struct graph{
    	int nxt,to;
    }e[N];
    ll w[N],siz[N],ans,sum;
    int fa[N],f[N],g[N],p[N],dep[N],n,m,k,t,rt,cnt,tot;
    bool sel[N];
    queue<int> q;
    inline int gf(int k){
    	if(f[k]==k) return k;
    	return f[k]=gf(f[k]);
    }
    inline int gfa(int k){
    	if(fa[k]==k) return k;
    	return fa[k]=gfa(fa[k]);
    }
    int hhh;
    inline int gfa1(int k){
    	if(++hhh>N) return 0;
    	printf("k=%d\n",k);
    	if(fa[k]==k) return k;
    	return fa[k]=gfa1(fa[k]);
    }
    inline void addedge(int x,int y){
    	e[++t]=(graph){g[x],y};g[x]=t;
    }
    inline void adde(int x,int y){
    	addedge(x,y);addedge(y,x);
    }
    inline void dfs1(int u){
    	siz[u]=w[u];
    	for(int i=g[u];i;i=e[i].nxt)
    		if(fa[u]!=e[i].to){
    			fa[e[i].to]=u;
    			dep[e[i].to]=dep[u]+1;
    			dfs1(e[i].to);
    			siz[u]+=siz[e[i].to];
    		}
    }
    inline void dfs(int u){
    	if(u>k){
    		t=0;
    		for(int i=1;i<=cnt;++i){
    			g[p[i]]=0,fa[p[i]]=p[i];
    		}
    		for(int i=1,x,y;i<=k;++i)
    			if(sel[i]){
    				x=gfa(b[i].l);y=gfa(b[i].r);
    				if(x==y) return;
    				fa[x]=y;adde(b[i].l,b[i].r);
    			}
    		for(int i=1,x,y;i<=tot;++i){
    			x=gfa(a[i].l);y=gfa(a[i].r);
    			if(x!=y){
    				fa[x]=y;adde(a[i].l,a[i].r);
    			}
    		}
    		for(int i=1;i<=cnt;++i) fa[p[i]]=0,f[p[i]]=INF;
    		dfs1(rt);
    		//对于此时树的形态,一条边所对应的路径权值肯定不能超过它
    		//f[u]:(u,fa[u])的权值<=f[u] 
    		for(int i=1,x,y;i<=tot;++i){
    			x=a[i].l;y=a[i].r;
    			if(dep[x]<dep[y]) swap(x,y);
    			while(dep[x]!=dep[y]){
    				f[x]=min(f[x],a[i].w);x=fa[x];
    			}
    			while(x!=y){
    				f[x]=min(f[x],a[i].w);f[y]=min(f[y],a[i].w);x=fa[x];y=fa[y];
    			}
    		}
    		sum=0;
    		for(int i=1,x,y;i<=k;++i)
    			if(sel[i]){
    				x=b[i].l;y=b[i].r;
    				if(dep[x]<dep[y]) swap(x,y);
    				sum+=1ll*siz[x]*f[x];
    			}
    		ans=max(ans,sum);
    		return;
    	}
    	sel[u]=true;dfs(u+1);
    	sel[u]=false;dfs(u+1);
    }
    inline void Aireen(){
    	n=read();m=read();k=read();
    	for(int i=1;i<=n;++i) fa[i]=f[i]=i;
    	for(int i=1;i<=m;++i){
    		a[i]=(edge){read(),read(),read()};
    	}
    	for(int i=1,x,y;i<=k;++i){
    		b[i]=(edge){read(),read(),0};
    		x=gf(b[i].l);y=gf(b[i].r);
    		if(x!=y) f[x]=y;
    	}
    	for(int i=1;i<=n;++i) w[i]=read();
    	
    	sort(a+1,a+1+m);
    	for(int i=1,x,y;i<=m;++i){
    		x=gf(a[i].l);y=gf(a[i].r);
    		if(x!=y){
    			f[x]=y;
    			x=gfa(a[i].l);y=gfa(a[i].r);
    			fa[x]=y;w[y]+=w[x];
    		}
    	}
    	
    	for(int i=1;i<=n;++i) f[i]=gfa(i);
    	for(int i=1;i<=m;++i){
    		a[i].l=fa[a[i].l],a[i].r=fa[a[i].r];
    	}
    	for(int i=1;i<=k;++i)
    		b[i].l=fa[b[i].l],b[i].r=fa[b[i].r];
    	
    	for(int i=1,x,y;i<=m;++i)
    		if(a[i].w){
    			x=gf(a[i].l);y=gf(a[i].r);
    			if(x!=y){f[x]=y,a[++tot]=a[i];
    			} 
    		}
    	
    	rt=fa[1];
    	for(int i=1;i<=n;++i)
    		if(fa[i]==i) p[++cnt]=i;
    	dfs(1);
    	printf("%lld\n",ans); 
    }
    

    2017-05-09 18:43:00

  • 相关阅读:
    穷举 迭代 while
    for 循环
    switch case
    if else 语句
    数据类型
    语句的输入、输出
    控件——DataGridview
    mysql-bin.000001文件的来源及处理方法
    /var/log目录下的20个Linux日志文件功能详解
    CountDownLatch
  • 原文地址:https://www.cnblogs.com/AireenYe/p/bzoj3206.html
Copyright © 2020-2023  润新知