【BZOJ3691】游行(网络流)

【BZOJ3691】游行(网络流)

题面

BZOJ
然而权限题。

Description

每年春季,在某岛屿上都会举行游行活动。
在这个岛屿上有N个城市,M条连接着城市的有向道路。
你要安排英雄们的巡游。英雄从城市si出发,经过若干个城市,到城市ti结束,需要特别注意的是,每个英雄的巡游的si可以和ti相同,但是必须至少途径2个城市。
每次游行你的花费将由3部分构成:
1.每个英雄游行经过的距离之和,需要特别注意的是,假如一条边被途径了k次,那么它对答案的贡献是k*ci,ci表示这条边的边权。
2.如果一个英雄的巡游的si不等于ti,那么会额外增加C的费用。因为英雄要打的回到起点。
3.如果一个城市没有任何一个英雄途经,那么这个城市会很不高兴,需要C费用的补偿。
你有无数个的英雄。你要合理安排游行方案,使得费用最小。
由于每年,C值都是不一样的。所以你要回答Q个询问,每个询问都是,当C为当前输入数值的时候的答案。

Input

第一行正整数N,M,Q;
接下来的M行,每行ai,bi,ci,表示有一条从ai到bi,边权为ci的有向道路。保证不会有自环,但不保证没有重边。
接下来Q行,每行一个C,表示询问当每次费用为C时的最小答案。

Output

Q行,每行代表一个询问的答案。

Sample Input

6 5 3

1 3 2

2 3 2

3 4 2

4 5 2

4 6 2

1

5

10

Sample Output

6

21

32

题解

没想到我竟然放了题面

发现\(C\)是在不断变化的,所以考虑计算一个和\(C\)无关的东西,最后再把\(C\)的贡献考虑进来。
先把两个和\(C\)相关的限制给统一起来,我们认为一条路径只覆盖其终点,其起点不被覆盖,那么最终所有的未被覆盖的点产生\(C\)的贡献。
这样子看起来还是有点区别的,主要问题在于经过了一个环,然后回到了起点\(S\),再走出去确定了一个终点\(T\),这样子看起来覆盖了所有点,但是仍要付出一个\(C\)的代价。
实际上这样子没错,但是这样子不优,因为你可以只走环,然后把剩下的路径给拆开,这样子代价仍然是一个\(C\),但是少走了一条边的代价。
实际上我们做的是一个最小路径覆盖,这样就会得到比上述东西更优的一个解。
两点之间连边边权为两点之间的最短路,然后跑最短路径覆盖,这样子每次都会新增一个点进入覆盖,并且权值是单增的。
那么只需要对于每次询问二分权值从哪里开始大于\(C\),前面的路径覆盖,后面的直接用\(C\)即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
#define MAX 255
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int val[MAX],sum[MAX],tot;
namespace MCMF
{
	const int MAXM=1000000,MAXN=1000;
	struct Line{int v,next,w,fy;}e[MAXM];
	int h[MAXN],cnt=2;
	inline void Add(int u,int v,int w,int fy)
	{
		e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
		e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
	}
	int dis[MAXN],pe[MAXN],pv[MAXN],Cost,Flow;
	bool vis[MAXN];queue<int> Q;
	int S=0,T=MAXN-1;
	bool SPFA()
	{
		memset(dis,63,sizeof(dis));dis[S]=0;
		Q.push(S);vis[S]=true;
		while(!Q.empty())
		{
			int u=Q.front();Q.pop();
			for(int i=h[u];i;i=e[i].next)
			{
				int v=e[i].v;if(!e[i].w)continue;
				if(dis[u]+e[i].fy<dis[v])
				{
					dis[v]=dis[u]+e[i].fy;pe[v]=i,pv[v]=u;
					if(!vis[v])vis[v]=true,Q.push(v);
				}
			}
			vis[u]=false;
		}
		if(dis[T]>=1e9)return false;
		int flow=1e9;
		for(int i=T;i!=S;i=pv[i])flow=min(flow,e[pe[i]].w);
		for(int i=T;i!=S;i=pv[i])e[pe[i]].w-=flow,e[pe[i]^1].w+=flow;
		Flow+=flow;Cost+=dis[T]*flow;
		val[++tot]=dis[T]*flow;sum[tot]=sum[tot-1]+val[tot];
		return true;
	}
}
using namespace MCMF;
int n,m,q,g[MAX][MAX];
int main()
{
	n=read();m=read();q=read();
	memset(g,63,sizeof(g));for(int i=1;i<=n;++i)g[i][i]=0;
	for(int i=1,u,v;i<=m;++i)u=read(),v=read(),g[u][v]=min(read(),g[u][v]);
	for(int k=1;k<=n;++k)
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			if(i^j)Add(i,j+n,1,g[i][j]);
	for(int i=1;i<=n;++i)Add(S,i,1,0),Add(i+n,T,1,0);
	while(SPFA());
	while(q--)
	{
		int C=read(),l=1,r=tot,ret=0;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(val[mid]<C)l=mid+1,ret=mid;
			else r=mid-1;
		}
		printf("%d\n",sum[ret]+(n-ret)*C);
	}
	return 0;
}
posted @ 2019-03-05 15:50  小蒟蒻yyb  阅读(491)  评论(0编辑  收藏  举报