BZOJ 1937: [Shoi2004]Mst 最小生成树
我称之为重拾KM(好久以前学的然后现在忘得差不多了)?
首先我们容易想到把每一条非树边拿出来,它显然会和一些树边形成一个环
那么那些树边是最小生成树上的边的充要条件显然是它们的边权都小于等于这条非树边
考虑树边的权值必然是减少的,非树边的权值必然是增加的,我们设\(x\)为树边,\(y\)为非树边,那么:
\[w_x-d_x\le w_y+d_y\Leftrightarrow d_x+d_y\ge w_x-w_y
\]
考虑这里的\(w_x-w_y\)是一个定值,那么我们发现上面那个式子恰好是KM算法中顶标与边权的关系
而这里的要求恰好是最小化\(\sum_i d_i\),同样满足KM结束的条件,因此直接做就是了
PS:貌似还可以直接对偶化成线性规划问题上单纯形的做法,不够没这个直观。好吧是我不会单纯形
#include<cstdio>
#include<vector>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1005,INF=1e9;
struct data
{
int x,y,w;
}e[N]; int n,m,x,y,id[N][N],anc[N],dep[N],w[N][N]; bool tr[N][N]; vector <int> E[N];
inline void addedge(CI x,CI y)
{
E[x].push_back(y); E[y].push_back(x);
}
inline void DFS(CI now=1,CI fa=0)
{
anc[now]=fa; dep[now]=dep[fa]+1;
for (vector <int>:: iterator to=E[now].begin();to!=E[now].end();++to)
if (*to!=fa) DFS(*to,now);
}
inline int LCA(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y); while (dep[x]!=dep[y]) x=anc[x];
while (x!=y) x=anc[x],y=anc[y]; return x;
}
namespace KM
{
int dx[N],dy[N],rsd[N],fr[N]; bool vx[N],vy[N];
inline bool find(CI now)
{
vx[now]=1; for (RI to=1;to<=m;++to) if (!vy[to])
{
int dlt=dx[now]+dy[to]-w[now][to];
if (!dlt) { vy[to]=1; if (!~fr[to]||find(fr[to])) return fr[to]=now,1; }
else rsd[to]=min(rsd[to],dlt);
}
return 0;
}
inline int KM(int ret=0)
{
RI i,j; for (i=1;i<=m;++i) dx[i]=-INF,fr[i]=-1;
for (i=1;i<=m;++i) for (j=1;j<=m;++j) dx[i]=max(dx[i],w[i][j]);
for (i=1;i<=m;++i)
{
for (j=1;j<=m;++j) rsd[j]=INF; for (;;)
{
for (j=1;j<=m;++j) vx[j]=vy[j]=0; if (find(i)) break;
int dlt=INF; for (j=1;j<=m;++j) if (!vy[j]) dlt=min(dlt,rsd[j]);
for (j=1;j<=m;++j) if (vx[j]) dx[j]-=dlt;
for (j=1;j<=m;++j) if (vy[j]) dy[j]+=dlt; else rsd[j]-=dlt;
}
}
for (i=1;i<=m;++i) if (~fr[i]) ret+=w[fr[i]][i]; return ret;
}
};
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d%d",&x,&y,&e[i].w),e[i].x=x,e[i].y=y,id[x][y]=id[y][x]=i;
for (i=1;i<n;++i) scanf("%d%d",&x,&y),tr[x][y]=tr[y][x]=1,addedge(x,y);
for (DFS(),i=1;i<=m;++i)
{
x=e[i].x; y=e[i].y; if (tr[x][y]) continue; int fa=LCA(x,y);
while (x!=fa) w[id[anc[x]][x]][i]=e[id[anc[x]][x]].w-e[i].w,x=anc[x];
while (y!=fa) w[id[anc[y]][y]][i]=e[id[anc[y]][y]].w-e[i].w,y=anc[y];
}
return printf("%d",KM::KM()),0;
}
辣鸡老年选手AFO在即