• BZOJ 1937 (luogu 4412) (KM+LCA)


    题面

    传送门

    分析

    根据贪心的思想我们得到几条性质:

    1.生成树上的边权减小,非树边的边权增加

    2.每条边最多被修改一次

    设改变量的绝对值为d

    对于一条非树边(j:(u,v)),树上u->v的路径上的任意一条边i的边权(w_ileq j),否则把i替换成j可以得到一棵更小的生成树

    因此有(w_i-d_i leq w_j+d_j)

    转换一下有(w_i-w_j leq d_i+d_j)

    发现形式和KM算法中的顶标很相似,所以把原图中的边看成点,变化值为顶标,新图的边权(w_i-w_j)

    跑KM算法即可

    实现中需要注意几个细节:

    由于两边的点数不一定=n,所以要加虚点,把两边的点补成n,左部虚点i和右部1~n连边,边权为0 ,右部同理

    代码中只要把邻接矩阵的初始值全部设为0即可,这样相当于该虚点和任意点都可以匹配,且权值为0

    匹配其他的点时如果发现不满足,就把虚点的匹配点换一下 ,KM算法结束后这个点随便匹配另一个点即可,因为权值为0,不影响答案

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<map>
    #include<utility>
    #define maxn 55
    #define maxm 805
    #define maxlog 32
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m;
    int is_tree[maxm];
    map<pair<int,int>,int>edge_id;
    struct edge {
    	int from;
    	int to;
    	int len;
    	int next;
    } G[maxm<<1],T[maxm<<1];
    int sz=1;
    int head[maxn];
    void add_edge1(int u,int v) {
    	sz++;
    	T[sz].from=u;
    	T[sz].to=v;
    	T[sz].next=head[u];
    	head[u]=sz;
    }
    
    int log2n;
    int deep[maxn];
    int anc[maxn][maxlog];
    void dfs1(int x,int fa) {
    	deep[x]=deep[fa]+1;
    	anc[x][0]=fa;
    	for(int i=1; i<=log2n; i++) {
    		anc[x][i]=anc[anc[x][i-1]][i-1];
    	}
    	for(int i=head[x]; i; i=T[i].next) {
    		int y=T[i].to;
    		if(y!=fa) {
    			dfs1(y,x);
    		}
    	}
    }
    
    int lca(int x,int y) {
    	if(deep[x]<deep[y]) swap(x,y);
    	for(int i=log2n; i>=0; i--) {
    		if(deep[anc[x][i]]>=deep[y]) x=anc[x][i];
    	}
    	if(x==y) return x;
    	for(int i=log2n; i>=0; i--) {
    		if(anc[x][i]!=anc[y][i]) {
    			x=anc[x][i];
    			y=anc[y][i];
    		}
    	}
    	return anc[x][0];
    }
    
    
    int dist[maxm][maxm];
    
    void add_edge2(int u,int v,int w){
    	w=max(w,0);
    //	printf("debug:%d %d
    ",u,v);
    	dist[u][v]=w; 
    }
    
    void make_graph(int x,int y,int ed){
    	int l=lca(x,y);
    	if(x==l){
    		for(int i=y;i!=l;i=anc[i][0]){
    			int t=edge_id[make_pair(i,anc[i][0])];
    			add_edge2(t,ed,G[t].len-G[ed].len);
    		}
    	}else if(y==l){
    		for(int i=x;i!=l;i=anc[i][0]){
    			int t=edge_id[make_pair(i,anc[i][0])];
    			add_edge2(t,ed,G[t].len-G[ed].len);
    		}
    	}else{
    		for(int i=x;i!=l;i=anc[i][0]){
    			int t=edge_id[make_pair(i,anc[i][0])];
    			add_edge2(t,ed,G[t].len-G[ed].len);
    		}
    		for(int i=y;i!=l;i=anc[i][0]){
    			int t=edge_id[make_pair(i,anc[i][0])];
    			add_edge2(t,ed,G[t].len-G[ed].len);
    		}
    	}
    }
    
    int la[maxm];
    int lb[maxm];
    int match[maxm];
    int va[maxm];
    int vb[maxm]; 
    int delta;
    int dfs2(int x){
    	va[x]=1;
    	for(int y=1;y<=m;y++){
    		if(!vb[y]){
    			if(la[x]+lb[y]==dist[x][y]){
    				vb[y]=1;
    				if(!match[y]||dfs2(match[y])){
    					match[y]=x;
    					return 1;
    				}
    			}
    		}
    	}
    	return 0;
    }
    
    int KM(){
    	for(int i=1;i<=m;i++){
    		la[i]=-INF;
    		for(int j=1;j<=m;j++){
    			la[i]=max(la[i],dist[i][j]);
    		}
    		lb[i]=0;
    	}
    	for(int i=1;i<=m;i++){
    		while(1){
    			memset(va,0,sizeof(va));
    			memset(vb,0,sizeof(vb));
    			delta=INF;
    			if(dfs2(i)) break;
    			for(int j=1;j<=m;j++){
    				if(!va[j]) continue;
    				for(int k=1;k<=m;k++){
    					if(!vb[k]){
    						delta=min(delta,la[j]+lb[k]-dist[j][k]);
    					}
    				}
    			}
    			for(int j=1;j<=m;j++){
    				if(va[j]) la[j]-=delta;
    				if(vb[j]) lb[j]+=delta;
    			}
    		}
    	}
    	int ans=0;
    	for(int i=1;i<=m;i++){
    		ans+=dist[match[i]][i];
    	}
    	return ans;
    }
    
    
    int main() {
    	scanf("%d %d",&n,&m);
    	log2n=log2(n)+1;
    	int u,v,w;
    	for(int i=1;i<=m;i++){
    		scanf("%d %d %d",&u,&v,&w);
    		G[i].from=u;
    		G[i].to=v;
    		G[i].len=w;
    		edge_id[make_pair(u,v)]=edge_id[make_pair(v,u)]=i;
    	}
    	int p;
    	for(int i=1;i<n;i++){
    		scanf("%d %d",&u,&v);
    		p=edge_id[make_pair(u,v)];
    		add_edge1(u,v);
    		add_edge1(v,u);
    		is_tree[p]=1;
    	}
    	dfs1(1,0);
    //	memset(dist,0x3f,sizeof(dist));
    	for(int i=1;i<=m;i++){
    		if(!is_tree[i]) make_graph(G[i].from,G[i].to,i);
    	}
    //	for(int i=1;i<=m;i++){
    //		for(int j=1;j<=m;j++){
    //			if(dist[i][j]==INF) printf("INF ",dist[i][j]);
    //			else printf("%d	",dist[i][j]);
    //		}
    //		printf("
    ");
    //	}
    	printf("%d
    ",KM());
    }
    
  • 相关阅读:
    『Argparse』命令行解析
    『MXNet』专题汇总
    用.NET开发通用Windows App
    ASP.NET 5探险(6):升级ASP.NET 5到beta6
    使用ASP.NET MVC、Rabbit WeixinSDK和Azure快速开发部署微信后台
    Visual Studio 2015将在7月20号RTM
    VS2015上又一必备免费插件:Refactoring Essentials
    ASP.NET 5探险(5):利用AzureAD实现单点登录
    Visual Studio Code升级到0.5,提供对ES6的更好支持
    ASP.NET 5探险(4):如何把ASP.NET 5从beta4升级到beta5
  • 原文地址:https://www.cnblogs.com/birchtree/p/10187657.html
Copyright © 2020-2023  润新知