bzoj1937[Shoi2004]Mst 最小生成树
题目链接:bzoj1937
题目大意:
题解:
KM/费用流
看不懂题解的我%了发男神
首先显然的,对于T上的边只进行减小操作,非T上的边只进行增加操作。(当然也可以不改变)
因为已经要求了一棵最小生成树T,即让非T上的边都不选。那么它不选的理由是什么呢?
假设这条非T的边编号为j,连接x->y,如果它不选的话,那么它的边权w[j]一定大于所以x->y在T中的路径上的边的边权,才会不选它。不然,因为图的连通性还是得到保证的,就会去掉一条边权比j大的边而选择j。
设delta[i]表示i这条边修改的量。于是就有w[i]-delta[i]<=w[j]+delta[j],移项后得w[i]-w[j]<=delta[i]+delta[j],然后不就可以把delta看成KM的顶标吗,而i->j的边权就是w[i]-w[j]啊。
吐槽..
我想练练KM而找的这题。。结果KM打得一塌糊涂qwq
而奥爷爷在我问了他的快一天后来做这题,然后在我之前A掉了这题qwq
直接上费用流32ms,,而我调了一年的KM八百多ms。。。
心好累啊
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 60
#define M 1000
const int inf=0xfffffff;
struct node
{
int x,y,c,next,id;
}T[M*2];
int d[M][M];
int len,first[M];
struct edge
{
int x,y,c;bool bk;
}a[M];
int id[N][N],tim,n,m;
int bf[M],slack[M],lx[M],ly[M];
int mymin(int x,int y){return (x<y)?x:y;}
int mymax(int x,int y){return (x>y)?x:y;}
void ins(int x,int y,int c,int p)
{
len++;T[len].x=x;T[len].y=y;T[len].c=c;
T[len].id=p;T[len].next=first[x];first[x]=len;
}
int visx[M],visy[M];
int dep[N],fa[N][10],upe[N];
bool vis[N];
void dfs(int x)
{
vis[x]=true;
for (int i=first[x];i!=-1;i=T[i].next)
{
int y=T[i].y;
if (!vis[y])
{
fa[y][0]=x;
dep[y]=dep[x]+1;
upe[y]=T[i].id;
dfs(y);
}
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) {int t=x;x=y;y=t;}
for (int i=8;i>=0;i--)
if (dep[x]-dep[y]>=(1<<i)) x=fa[x][i];
if (x==y) return x;
for (int i=8;i>=0;i--)
if (dep[x]>(1<<i) && fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
void bt(int x,int p,int k)
{
while (x!=p)
{
d[upe[x]][k]=mymax(0,a[upe[x]].c-a[k].c);
x=fa[x][0];
}
}
bool ffind(int x)
{
visx[x]=tim;
for (int y=1;y<=m;y++)
if (visy[y]!=tim)
{
if (lx[x]+ly[y]==d[x][y])
{
visy[y]=tim;
if (bf[y]==-1 || ffind(bf[y]))
{
bf[y]=x;
return true;
}
}else slack[y]=mymin(slack[y],lx[x]+ly[y]-d[x][y]);
}
return false;
}
void KM()
{
tim=0;int i,j;
for (i=1;i<=m;i++) lx[i]=-inf,visx[i]=ly[i]=visy[i]=0,bf[i]=-1;
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
lx[i]=mymax(lx[i],d[i][j]);
for (i=1;i<=m;i++)
{
for (j=1;j<=m;j++) slack[j]=inf;
while (1)
{
tim++;
if (ffind(i)) break;
int delta=inf;
for (j=1;j<=m;j++) if (visy[j]!=tim) delta=mymin(delta,slack[j]);
for (j=1;j<=m;j++)
{
if (visx[j]==tim) lx[j]-=delta;
if (visy[j]==tim) ly[j]+=delta;
else slack[j]-=delta;
}
}
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int i,j,x,y,c;
scanf("%d%d",&n,&m);
len=0;memset(first,-1,sizeof(first));
for (i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&c);
if (x>y) {int t=x;x=y;y=t;}
a[i].x=x;a[i].y=y;a[i].c=c;
id[x][y]=i;a[i].bk=false;
}
for (i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
if (x>y) {int t=x;x=y;y=t;}
ins(x,y,a[id[x][y]].c,id[x][y]);
ins(y,x,a[id[x][y]].c,id[x][y]);
a[id[x][y]].bk=true;
vis[i]=false;
}
vis[n]=false;dep[1]=1;dfs(1);
for (j=1;j<=8;j++)
for (i=2;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for (i=1;i<=m;i++) if (!a[i].bk)
{
int lc=lca(a[i].x,a[i].y);
bt(a[i].x,lc,i);
bt(a[i].y,lc,i);
}
KM(); int ans=0;
for (i=1;i<=m;i++) if (a[i].bk) ans+=lx[i];else ans+=ly[i];
printf("%d\n",ans);
return 0;
}