zoukankan      html  css  js  c++  java
  • [APIO2013]道路费用

    题目描述

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

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

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

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

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

    题解

    我们需要让这k条边的代价最大。

    看到k很小,可以想到枚举每条边的选择情况,但如果直接每次跑一遍最小生成树会使复杂度变得很高。

    首先我们把这(k)条边先加进去,然后再对其他边跑最小生成树,那么此时加进去的边一定是必须在最后的生成树里的。

    那么我们把这些边提前加进去,这样会形成(k+1)个联通块,我们把这些联通块看做点,那么点数的级别就变成了(O(K))的。

    然后我们还发现有些边是没用的,我们先把联通块缩点,然后用(kruskal)尝试填加其他边,那么我们只会填加(k)条边,可以发现,此时还没有添加进去的边是没有用的,这些边就是可能会加进去的边,这样边数也是(O(K))的了。

    对于新边的权值的设定,就是所有跨过它的边的权值最小值。

    然后这道题就变成大模拟了。。

    代码

    #include<bits/stdc++.h>
    #define N 100009
    #define M 300009
    #define K 22
    #define inf 2e9
    using namespace std;
    typedef long long ll;
    int f[N],n,m,k,l[N],r[N],dep[N],be[N],tot,num[N],tong[N],ma[K],mx[N],ji[K];
    ll size[N],w[N],sum[N],ans;
    bool vis[N];
    int head[K],tott; 
    struct edge{
    	int n,to,l;
    }e[K<<1];
    inline void add(int u,int v,int l){
    	e[++tott].n=head[u];e[tott].to=v;head[u]=tott;e[tott].l=l;
    }
    inline ll rd(){
    	ll x=0;char c=getchar();bool f=0;
    	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f?-x:x;
    }
    int find(int x){return f[x]=f[x]==x?x:find(f[x]);}
    inline int zh(int x){return tong[be[x]];}
    struct node{
    	int u,v,w;
    	inline bool operator <(const node &b)const{return w<b.w;}
    }b[M];
    ll dfs(int u,int fa){
    	size[u]=sum[u];
    	ll ans=0;
    	for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
    		int v=e[i].to;
    		ans+=dfs(v,u);
    		size[u]+=size[v];
    		if(e[i].l)ans+=size[v]*mx[v];
        }
    	return ans;
    }
    void dfs0(int u,int fa){
    	if(!fa)f[u]=u;
    	for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
    		int v=e[i].to;f[v]=u;dep[v]=dep[u]+1;
    		dfs0(v,u);
        }
    }
    int main(){
    	n=rd();m=rd();k=rd();
    	for(int i=1;i<=m;++i){
    		b[i].u=rd();b[i].v=rd();b[i].w=rd();
    	}
    	sort(b+1,b+m+1);
    	for(int i=1;i<=n;++i)f[i]=i;
    	for(int i=1;i<=k;++i){
    		l[i]=rd();r[i]=rd();
    		int xx=find(l[i]),yy=find(r[i]);
    		if(xx!=yy)f[xx]=yy;
    	}
    	for(int i=1;i<=n;++i)w[i]=rd();
    	for(int i=1;i<=m;++i){
    		int xx=find(b[i].u),yy=find(b[i].v);
    		if(xx!=yy)f[xx]=yy,num[++num[0]]=i,vis[i]=1;
    	}
    	for(int i=1;i<=n;++i)f[i]=i;
    	for(int i=1;i<=num[0];++i){
    		int xx=find(b[num[i]].u),yy=find(b[num[i]].v);
    		if(xx!=yy)f[xx]=yy;
    	}
    	for(int i=1;i<=n;++i){
    	  be[i]=find(i);
    	  if(!tong[be[i]])tong[be[i]]=++tot;
    	  sum[tong[be[i]]]+=w[i];
        }
    	for(int i=1;i<=m;++i){
    		int xx=find(b[i].u),yy=find(b[i].v);
    		if(xx!=yy){
    		  f[xx]=yy;
    		  ma[++ma[0]]=i;
    	    }
    	}
    	for(int i=0;i<(1<<k);++i){ 
    		tott=0;ji[0]=0;
    		memset(head,0,sizeof(head));
    		for(int j=1;j<=tot;++j){
    		  f[j]=j,dep[j]=0;mx[j]=inf;
    	    }
    		for(int j=1;j<=k;++j)if(i&(1<<j-1)){
    			int xx=find(zh(l[j])),yy=find(zh(r[j]));
    			if(xx!=yy){
    			  f[xx]=yy;
    			  int x=zh(l[j]),y=zh(r[j]);
    			  add(x,y,1);add(y,x,1);
    		    }
    		}
    		for(int j=1;j<=ma[0];++j){
    		    int xx=find(zh(b[ma[j]].u)),yy=find(zh(b[ma[j]].v));
    		    if(xx!=yy){
    		    	f[xx]=yy;
    		    	int x=zh(b[ma[j]].u),y=zh(b[ma[j]].v);
    		        add(x,y,0);add(y,x,0);
    			}
    			else{
    				ji[++ji[0]]=ma[j];
    			}
    		}
    		dfs0(zh(1),0);
    		for(int j=1;j<=ji[0];++j){
    			int xx=zh(b[ji[j]].u),yy=zh(b[ji[j]].v);	
    			if(dep[xx]<dep[yy])swap(xx,yy);
    			while(dep[xx]>dep[yy])mx[xx]=min(mx[xx],b[ji[j]].w),xx=f[xx];
    			while(xx!=yy){
    				mx[xx]=min(mx[xx],b[ji[j]].w);
    				mx[yy]=min(mx[yy],b[ji[j]].w);
    				xx=f[xx];yy=f[yy];
    			}
    		}
    		ans=max(ans,dfs(zh(1),0));
    	}
        printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Win2000下系统进程隐藏代码
    关于Windows下ShellCode编写的一点思考
    木马是如何编写的(三)
    MS04011远程缓冲区溢出代码
    关于Windows下ShellCode编写的一点思考
    木马是如何编写的(三)
    运用GIMP制作平面图片
    PCMan File Manager:标签式的文件解决软件
    AutoMySQLBackup——主动备份 MySQL 数据库的剧本
    ubuntu8.04下USB转UART接口的应用
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10804256.html
Copyright © 2011-2022 走看看