• 【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
    ",sum[ret]+(n-ret)*C);
    	}
    	return 0;
    }
    
  • 相关阅读:
    data:image/png;base64
    需要去了解的知识
    【转】react的高阶组件
    几个css问题
    antd中form中resetFields清空输入框
    react中map循环中key取值问题
    react中父组件调用子组件的方法
    hive 初始化 时间问题 The server time zone value 'EDT' is unrecognized
    centos7安装MySQL8 无法修改密码 无法修改密码策略
    虚拟机 Linux 不能连 xshell 不能上网
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10477294.html
Copyright © 2020-2023  润新知