[日常训练]Z国特色社会路

Description

\(W\)非常喜欢社会主义,这天他开始研究它的优越性。
他发现它们国家十分乐于修建特色的社会主义道路。具体的说,\(Z\)国有\(n\)座城市,由\(m\)条有向边连接,城市从\(1\)编号。
特色的地方在于,时不时会有一些\(L\)下来在城市间视察,视察时他会从城市\(b_i\)开始,最终到\(e_i\)结束。每次视察都会走过一些路,这些路自然会被\(L\)所注意。
更具体地, \(L\)会重修自己走过的路。每条边重修需要的费用也不相同。
而如果视察结束后, \(L\)不在一开始自己所在的城市\(b_i\),则会要新建一条\(VIP\)道路送他回家,也就是只有他自己能通过的道路。这需要花费固定的费用\(C\),这条道路走过后便会拆毁。
若某个城市没有被\(L\)经过,则这个城市的下级\(L\)会被勒令整改,也要花费\(C\)的费用。
现在有\(k\)年,每年有若干个\(L\)下来视察(可能\(0\)个),每年的固定费用\(C\)不同。小\(W\)想知道对于每一年怎样安排他们的视察人数和视察路线,能使得总花费最小。注意,\(L\)至少要视察一条边。
注意,若一条道路被同一个人多次经过,则每次都会重修这条路。多个人多次经过也是一样。没有被\(L\)经过的城市,更具体的说是没有被任何\(L\)经过。

Input

第一行三个整数\(n,m,k\)

接下来\(m\)行每行三个整数\(s_i,t_i,v_i\),表示\(s_i\)\(t_i\)间的有向边,重修需要花费\(v_i\)的代价。接下来\(k\)行每行一个整数,表示这一年的固定费用\(C\)

Output

输出\(k\)行,每行一个最小花费。

Sample Input

6 5 3 6
1 3 2
2 3 2 
3 4 2
4 5 2
4 6 2
1
5
10

Sample Output

6
21
32

HINT

\(2\;\leq\;n\;\leq\;250,1\;\leq\;m\;\leq\;30000,1\;\leq\;k\;\leq\;10000,s_i\not=t_i,1\;\leq\;v_i,C\;\leq\;10000\),一对城市间可能有多条单向路,图中无自环。

Solution

先求出原图的最短路建一张完全图,任意两点间的边权为原图中两点的最短路长度.

可以发现,每个点只会被最优路径经过一次.

由于一个点只被经过一次,且要找出一些路径使得它们的和最小,这非常像最小路径覆盖的模型,拆点建边.

接着做最小费用最大流,若流了\(k\)路径,则我们需支付\((n-k)C\).

证明:若这\(k\)路径首尾相连成一个环,则需对剩下\(n-k\)个城市支付\(C\);若不是环,则这\(k\)路径经过\(k+1\)个点,需对剩下\(n-k-1\)个城市支付\(C\),以及为\(L\)修路回去的\(C\).
接下来要决定流几条路径.显然,做费用流时,每次得到的路径的费用是单调不降的,也就是可以在做费用流时判断:若当前的费用\(>C\),则停止费用流,剩下的城市用\(C\)补。

由于有多组\(C\),所以考虑提前将所有\(n\)次流后所得到的费用用前缀和存下来,每次二分出需要流的路径条数.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 505
#define M 125505
#define INF 1000000000
using namespace std;
struct graph{
    int nxt,to,f,w;
}e[M];
struct edge{
    int s,t,w;
}a[M];
struct tag{
    int e,v;
}pre[N];
int d[N][N],g[N],f[N],sum[N],dis[N],c,n,m,s,t,ti,cnt=1;
bool inq[N];queue<int> q; 
inline int read(){
    int ret=0;char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c)){
        ret=(ret<<1)+(ret<<3)+c-'0';
        c=getchar();
    }
    return ret;
}
inline void addedge(int x,int y,int f,int w){
    e[++cnt].nxt=g[x];g[x]=cnt;
    e[cnt].to=y;e[cnt].f=f;e[cnt].w=w;
}
inline void adde(int x,int y,int f,int w){
    addedge(x,y,f,w);addedge(y,x,0,-w);
}
inline bool spfa(int u){
    for(int i=1;i<=t;++i){
        dis[i]=INF;inq[i]=false;
    }
    dis[u]=0;q.push(u);inq[u]=true;
    while(!q.empty()){
        u=q.front();q.pop();inq[u]=false;
        for(int i=g[u];i;i=e[i].nxt)
            if(e[i].f>0&&dis[u]+e[i].w<dis[e[i].to]){
                dis[e[i].to]=dis[u]+e[i].w;
                pre[e[i].to].e=i;pre[e[i].to].v=u;
                if(!inq[e[i].to]){
                    q.push(e[i].to);inq[e[i].to]=true;
                }
            }
    }
    return dis[t]<INF;
}
inline int mf(int f){
    int ret=0,d;
    while(f){
        if(!spfa(s)) return -1;
        d=f;
        for(int i=t;i!=s;i=pre[i].v) 
            d=min(d,e[pre[i].e].f);
        ret+=d*dis[t];f-=d;
        for(int i=t;i!=s;i=pre[i].v){
            e[pre[i].e].f-=d;
            e[pre[i].e^1].f+=d;
        }
    }
    return ret;
}
inline void Aireen(){
    n=read();m=read();ti=read();
    for(int i=1;i<n;++i)
        for(int j=i+1;j<=n;++j)
            d[i][j]=d[j][i]=INF;
    for(int i=1,j,k,w;i<=m;++i){
        j=read();k=read();w=read();
        d[j][k]=min(d[j][k],w);
    }
    for(int k=1;k<=n;++k)
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    t=n+1<<1;s=t-1;
    for(int i=1;i<=n;++i){
        adde(s,i,1,0);
        adde(i+n,i,1,0);
        adde(i+n,t,1,0);
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(i!=j&&d[i][j]<INF)
                adde(i,j+n,1,d[i][j]);
    for(m=1;m<=n;++m){
        f[m]=mf(1);
        if(f[m]<0) break;
        sum[m]=sum[m-1]+f[m];
    }
    --m;
    int l,r,mid;
    while(ti--){
        c=read();
        l=0;r=m;
        while(l<r){
            mid=l+r+1>>1; 
            if(f[mid]<=c) l=mid;
            else r=mid-1;
        }
        printf("%d\n",sum[l]+c*(n-l));
    }
}
int main(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    Aireen();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2017-01-12 14:19  Aireen_Ye  阅读(251)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.