1. 程式人生 > >BZOJ 1937 (luogu 4412) (KM+LCA)

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());
}