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


    Description

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

    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 ot=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$,以及为$LD$修路回去的$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
    ",sum[l]+c*(n-l));
        }
    }
    int main(){
        freopen("road.in","r",stdin);
        freopen("road.out","w",stdout);
        Aireen();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
  • 相关阅读:
    JavaScript面向对象之闭包的理解
    JavaScript面向对象之函数构造器的理解
    记录jq控制select 选中状态
    JavaScript面向对象之创建类和方法
    获取对象属相 点运算符 和方括号 运算符
    parseFloat 和 Number isNaN 转换
    Js数据类型和运算符
    break , continue 和 标签 跳出循环
    javaScript--循环语句
    三元运算符
  • 原文地址:https://www.cnblogs.com/AireenYe/p/6278066.html
Copyright © 2020-2023  润新知