1. 程式人生 > >BZOJ1937: [Shoi2004]Mst 最小生成樹(洛谷P4412)

BZOJ1937: [Shoi2004]Mst 最小生成樹(洛谷P4412)

二分圖完美匹配

神仙一樣。。。

首先有個顯然的貪心:樹邊權值只減不增,非樹邊權值只增不減。

對於一條連線xxyy的非樹邊iixxyy路徑上所有的邊jj都要滿足wjdjwi+diw_j-d_j\leq w_i+d_i。移項後可得wjwidi+djw_j-w_i\leq d_i+d_j。左邊是個定值,右邊可以看成KM左右兩排點的頂標。然後刷KM就好了。

程式碼:

#include<cctype>
#include<cstdio>
#include<cstring>
#include
<algorithm>
#define N 805 #define F inline using namespace std; struct edg{ int x,y,z; }e[N]; struct edge{ int nxt,to,d; }ed[N<<1]; int n,m,k,ti,ans,h[N],fa[N],lx[N],ly[N],dep[N],id[N]; int fx[N],fy[N],slk[N],p[N],mp[N][N]; bool f[N]; F char readc(){ static char buf[100000],*l=buf,*r=buf; if
(l==r) r=(l=buf)+fread(buf,1,100000,stdin); return l==r?EOF:*l++; } F int _read(){ int x=0; char ch=readc(); while (!isdigit(ch)) ch=readc(); while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc(); return x; } #define add(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k F void dfs1(int x){ dep[x]
=dep[fa[x]]+1; for (int i=h[x],v;i;i=ed[i].nxt) if ((v=ed[i].to)!=fa[x]) fa[v]=x,id[v]=ed[i].d,dfs1(v); } F void find(int i){ int x=e[i].x,y=e[i].y,z=e[i].z; if (dep[x]<dep[y]) swap(x,y); while (dep[fa[x]]>=dep[y]) mp[id[x]][i]=max(0,e[id[x]].z-z),x=fa[x]; if (x==y) return; while (fa[x]!=fa[y]){ mp[id[x]][i]=max(0,e[id[x]].z-z),x=fa[x]; mp[id[y]][i]=max(0,e[id[y]].z-z),y=fa[y]; } mp[id[x]][i]=max(0,e[id[x]].z-z); mp[id[y]][i]=max(0,e[id[y]].z-z); } F bool dfs(int x){ fx[x]=ti; for (int y=1,v;y<=m;y++){ if (fy[y]==ti) continue; v=lx[x]+ly[y]-mp[x][y]; if (!v){ fy[y]=ti; if (!p[y]||dfs(p[y])) return p[y]=x,true; } else slk[y]=min(slk[y],v); } return false; } F void KM(){ for (int i=1;i<=m;i++) for (int j=1;j<=m;j++) lx[i]=max(lx[i],mp[i][j]); for (int i=1;i<=m;i++){ memset(slk,63,sizeof(slk)); while (1){ ti++; if (dfs(i)) break; int mn=1e9; for (int j=1;j<=m;j++) if (fy[j]!=ti) mn=min(mn,slk[j]); for (int j=1;j<=m;j++){ if (fx[j]==ti) lx[j]-=mn; fy[j]==ti?ly[j]+=mn:slk[j]-=mn; } } } for (int i=1;i<=m;i++) ans+=mp[p[i]][i]; } int main(){ n=_read(),m=_read(); for (int i=1;i<=m;i++) e[i].x=_read(),e[i].y=_read(),e[i].z=_read(); for (int i=1;i<=m;i++) if (e[i].x>e[i].y) swap(e[i].x,e[i].y); for (int i=1,x,y;i<n;i++){ x=_read(),y=_read(); if (x>y) swap(x,y); for (int j=1;j<=m;j++) if (e[j].x==x&&e[j].y==y){ f[j]=true,add(x,y,j),add(y,x,j); break; } } dfs1(1); for (int i=1;i<=m;i++) if (!f[i]) find(i); return KM(),printf("%d\n",ans),0; }