BZOJ 1937 (luogu 4412) (KM+LCA)
阿新 • • 發佈:2018-12-27
題面
分析
根據貪心的思想我們得到幾條性質:
幾條性質:
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()); }