题面

传送门

分析

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

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)的更多相关文章

  1. BZOJ 3052/Luogu P4074 [wc2013]糖果公园 (树上带修莫队)

    题面 中文题面,难得解释了 BZOJ传送门 Luogu传送门 分析 树上带修莫队板子题... 开始没给分块大小赋初值T了好一会... CODE #include <bits/stdc++.h&g ...

  2. BZOJ 3931 / Luogu P3171 [CQOI2015]网络吞吐量 (最大流板题)

    题面 中文题目,不解释: BZOJ传送门 Luogu传送门 分析 这题建图是显然的,拆点后iii和i′i'i′连容量为吞吐量的边,根据题目要求,111和nnn的吞吐量看作∞\infty∞. 然后用di ...

  3. BZOJ 3894 / Luogu P4313 文理分科 (拆点最小割)

    题面 中文题面- BZOJ 传送门 Luogu 传送门 分析 这道题类似于BZOJ 3774 最优选择,然后这里有一篇博客写的很好- Today_Blue_Rainbow's Blog 应该看懂了吧- ...

  4. BZOJ 2039 / Luogu P1791 [2009国家集训队]employ人员雇佣 (最小割)

    题面 BZOJ传送门 Luogu传送门 分析 考虑如何最小割建图,因为这仍然是二元关系,我们可以通过解方程来确定怎么建图,具体参考论文 <<浅析一类最小割问题 湖南师大附中 彭天翼> ...

  5. BZOJ 2127 / Luogu P1646 [国家集训队]happiness (最小割)

    题面 BZOJ传送门 Luogu传送门 分析 这道题又出现了二元关系,于是我们只需要解方程确定怎么连边就行了 假设跟SSS分在一块是选文科,跟TTT分在一块是选理科,先加上所有的收益,再来考虑如何让需 ...

  6. [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)

    [BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表) 题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段 ...

  7. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

  8. [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)

    [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...

  9. Bzoj 2286 & Luogu P2495 消耗战(LCA+虚树+欧拉序)

    题面 洛谷 Bzoj 题解 很容易想到$O(nk)$的树形$dp$吧,设$f[i]$表示处理完这$i$颗子树的最小花费,同时再设一个$mi[i]$表示$i$到根节点$1$路径上的距离最小值.于是有: ...

随机推荐

  1. sqlserver sp_who2和inputbuffer的使用,连接数

    一.sp_who2的使用 1.存储过程的位置 sp_who官方解释地址:https://docs.microsoft.com/zh-cn/sql/relational-databases/system ...

  2. STM32之模拟串口设计

    一.设计用途: 公司PCB制成板降成本,选择的MCU比项目需求少一个串口,为满足制成板成本和项目对串口需求,选择模拟一路串口. 二.硬件电路: 三.设计实现: 工具&软件:STM32F030R ...

  3. No qualifying bean of type xxx' available 的一种解决方法

    获取bean Class beanClass = Class.forName(event.className); FilterEvent filterEvent = (FilterEvent)Bean ...

  4. 线程数设置和CPU数的关系

    一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数) 如果是CPU密集型应用,则线程池大小设置为N+1 如果是IO密集型应用,则线程池大小设置为2N+1(因为io读数据或者缓存的 ...

  5. hdu 4992 Primitive Roots 【求原根模板】

    题目链接 大题流程: 判定是否有原根->求出最小原根->利用最小原根找出全部原根 #include<bits/stdc++.h> using namespace std; ty ...

  6. hdu 6047: Maximum Sequence (2017 多校第二场 1003)【贪心】

    题目链接 可以贪心写,先把b数组按从小到大的顺序排个序,根据b[i]的值来产生a[n+i] 借助一个c数组,c[i]记录,j从i到n,a[j]-j的最大值,再加上一个实时更新的变量ma,记录从n+1到 ...

  7. Django2 + python3 上传图片

    . ├── db.sqlite3 ├── manage.py ├── myImg │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init_ ...

  8. MySQL基础篇——安装、管理

    MySQL 安装 所有平台的 MySQL 下载地址为https://dev.mysql.com/downloads/mysql/ .挑选你需要的 MySQL Community Server 版本及对 ...

  9. php similar_text()函数 语法

    php similar_text()函数 语法 作用:计算两个字符串的相似度,并返回匹配字符的数目大理石平台厂家 语法:similar_text(string1,string2,percent) 参数 ...

  10. iOS7上leftBarButtonItem无法实现滑动返回的完美解决方案

    今天遇到了在iOS7上使用leftBarButtonItem却无法响应滑动返回事件的问题,一番谷歌,最后终于解决了,在这里把解决方案分享给大家. 在iOS7之前的系统,如果要自定义返回按钮,直接设置b ...