BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)

时间:2023-03-09 02:08:58
BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)

  虽然不一定每次都是由编号小的点向编号大的走,但一个人摧毁的顺序一定是从编号小的到编号大的。那么在摧毁据点x的过程中,其只能经过编号小于x的点。并且这样一定合法,因为可以控制其他人先去摧毁所经过的点。那么可以floyd求出由摧毁x到摧毁y的最短路径。注意这里也需要更新起点编号大于终点的情况,否则floyd会挂掉。

  剩下的问题就是用k条路径覆盖所有点使费用最小。那么考虑网络流。可以将每个点拆成入点和出点来控制节点流量至少为1,边权的费用设置为其间最短路径,跑上下界费用流即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 310
#define M 20010
#define S 302
#define T 303
#define in(x) (x<<1)
#define out(x) (x<<1|1)
int n,m,k,p[N],dis[N][N],t=-,ans=;
int d[N],q[N],pre[N];
bool flag[N];
struct data{int to,nxt,cap,flow,cost;
}edge[M<<];
void addedge(int x,int y,int z,int c)
{
t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=,edge[t].cost=c,p[x]=t;
t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=,edge[t].flow=,edge[t].cost=-c,p[y]=t;
}
int inc(int &x){x++;if (x>T+) x-=T+;return x;}
bool spfa()
{
memset(d,,sizeof(d));d[S]=;
memset(flag,,sizeof(flag));
int head=,tail=;q[]=S;
do
{
int x=q[inc(head)];flag[x]=;
for (int i=p[x];~i;i=edge[i].nxt)
if (d[x]+edge[i].cost<d[edge[i].to]&&edge[i].flow<edge[i].cap)
{
d[edge[i].to]=d[x]+edge[i].cost;
pre[edge[i].to]=i;
if (!flag[edge[i].to]) q[inc(tail)]=edge[i].to,flag[edge[i].to]=;
}
}while (head!=tail);
return d[T]<=;
}
void ekspfa()
{
while (spfa())
{
int v=k;
for (int i=T;i!=S;i=edge[pre[i]^].to)
v=min(v,edge[pre[i]].cap-edge[pre[i]].flow);
for (int i=T;i!=S;i=edge[pre[i]^].to)
ans+=v*edge[pre[i]].cost,edge[pre[i]].flow+=v,edge[pre[i]^].flow-=v;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2324.in","r",stdin);
freopen("bzoj2324.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read(),k=read();
memset(p,,sizeof(p));
memset(dis,,sizeof(dis));
for (int i=;i<=n;i++) dis[i][i]=;
for (int i=;i<=m;i++)
{
int x=read(),y=read(),z=read();
dis[x][y]=dis[y][x]=min(dis[x][y],z);
}
for (int k=;k<=n;k++)
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
if (i>=k||j>=k) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (int i=;i<n;i++)
for (int j=i+;j<=n;j++)
addedge(out(i),in(j),k,dis[i][j]);
for (int i=;i<=n;i++)
addedge(in(i),out(i),k,);
for (int i=;i<=n;i++)
addedge(in(i),T,,),addedge(S,out(i),,),addedge(out(i),out(n),k,);
addedge(out(n),in(),k,);
ekspfa();
cout<<ans;
return ;
}