BZOJ 1937 (luogu 4412) (KM+LCA)
题面
分析
根据贪心的思想我们得到几条性质:
1.生成树上的边权减小,非树边的边权增加
2.每条边最多被修改一次
设改变量的绝对值为d
对于一条非树边\(j:(u,v)\),树上u->v的路径上的任意一条边i的边权\(w_i\leq 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\n",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("\n");
//	}
	printf("%d\n",KM());
}
BZOJ 1937 (luogu 4412) (KM+LCA)的更多相关文章
- BZOJ 3052/Luogu P4074 [wc2013]糖果公园 (树上带修莫队)
		题面 中文题面,难得解释了 BZOJ传送门 Luogu传送门 分析 树上带修莫队板子题... 开始没给分块大小赋初值T了好一会... CODE #include <bits/stdc++.h&g ... 
- BZOJ 3931 / Luogu P3171 [CQOI2015]网络吞吐量 (最大流板题)
		题面 中文题目,不解释: BZOJ传送门 Luogu传送门 分析 这题建图是显然的,拆点后iii和i′i'i′连容量为吞吐量的边,根据题目要求,111和nnn的吞吐量看作∞\infty∞. 然后用di ... 
- BZOJ 3894 / Luogu P4313 文理分科 (拆点最小割)
		题面 中文题面- BZOJ 传送门 Luogu 传送门 分析 这道题类似于BZOJ 3774 最优选择,然后这里有一篇博客写的很好- Today_Blue_Rainbow's Blog 应该看懂了吧- ... 
- BZOJ 2039 / Luogu P1791 [2009国家集训队]employ人员雇佣 (最小割)
		题面 BZOJ传送门 Luogu传送门 分析 考虑如何最小割建图,因为这仍然是二元关系,我们可以通过解方程来确定怎么建图,具体参考论文 <<浅析一类最小割问题 湖南师大附中 彭天翼> ... 
- BZOJ 2127 / Luogu P1646 [国家集训队]happiness (最小割)
		题面 BZOJ传送门 Luogu传送门 分析 这道题又出现了二元关系,于是我们只需要解方程确定怎么连边就行了 假设跟SSS分在一块是选文科,跟TTT分在一块是选理科,先加上所有的收益,再来考虑如何让需 ... 
- [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)
		[BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表) 题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段 ... 
- [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)
		[BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ... 
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
		[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ... 
- Bzoj 2286 & Luogu P2495 消耗战(LCA+虚树+欧拉序)
		题面 洛谷 Bzoj 题解 很容易想到$O(nk)$的树形$dp$吧,设$f[i]$表示处理完这$i$颗子树的最小花费,同时再设一个$mi[i]$表示$i$到根节点$1$路径上的距离最小值.于是有: ... 
随机推荐
- 升级docker至最新版本
			升级docker至最新版本 1.查找主机上关于Docker的软件包 [root@pre1 ~]# rpm -qa | grep docker docker-1.13.1-88.git07f3374.e ... 
- R语言封装函数
			R语言封装函数 原帖见豆瓣:https://www.douban.com/note/279077707/ 一个完整的R函数,需要包括函数名称,函数声明,函数参数以及函数体几部分. 1. 函数名称,即要 ... 
- R语言——ggplot2补充知识点
			案例 ggplot(head(age_data,10),aes(x=reorder(Country,age_median),y=age_median))+ geom_bar(aes(fill=Coun ... 
- Unity 官网无法访问|国外网站访问过慢|国外网站访问加速器
			目录 1. 文档地址 2. 按 3. 工具下载地址 1. 文档地址 GitHub博客 https://coco5666.github.io/blog/articles/20190704-01/ 2. ... 
- Mysql查询结果导出Excel表
			Mysql查询结果导出Excel表: 一句转换方式:$ mysql -uops -p'GCNgH000KP' dtbs -e 'select * from t_proxy__record;' --de ... 
- java并发学习--第三章 线程安全问题
			线程的安全问题一直是我们在开发过程中重要关注的地方,出现线程安全问题的必须满足两个条件:存在着两个或者两个以上的线程:多个线程共享着共同的一个资源, 而且操作资源的代码有多句.接下来我们来根据JDK自 ... 
- springboot通过继承OncePerRequestFilter,在拦截器中@Autowired 为null问题
			springboot2版本以上环境 通过继承OncePerRequestFilter类,在重写doFilterInternal方法实现拦截的具体业务逻辑, 在实现的过程中,需要注入service方法, ... 
- CSS3 ::before和::after伪元素的实际应用
			实例 1.清除浮动 通常我们清除清除浮动的方式就是在浮动元素后面添加一个空的Div标签,然后在设置它的清除浮动要是,使用after伪元素,我们就不需要添加无意义的div标签在html中了,下面的例子就 ... 
- Vue 组件间的传值(通讯)
			组件之间的通讯分为三种 父给子传 子给父传 兄弟组件之间的通讯 1 父组件给子组件传值 子组件嵌套在父组件内部,父组件给子组件传递一个标识,在子组件内部用props接收,子组件在模板里可以通过{{}} ... 
- error: call of overloaded ‘sqrt(double&)’ is ambiguous
			OpenFOAM定义了新的sqrt,当引入新的Library时,必须显式地使用std::sqrt(),否则会报如下错误: error: call of overloaded 'sqrt(double& ... 
