【Codeforces 1187G】Gang Up

分层图费用流。

题目大意:

给定一张 $n$ 个点 $m$ 条边的无向连通图,有 $k$ 个人分别从一些点,想走到 $1$ 号点。

走 $1$ 条边要 $1$ 个时刻,一个时刻可以走 $1$ 条边也可以不走。

一个人如果 $t$ 时刻才到 $1$ 号点,要花费 $c\cdot t$ 的代价。

一条边一个方向上某一时刻如果有 $a$ 个人同时经过,要花费 $d\cdot a^2$ 的代价。

$c$ 和 $d$ 都是给定常数。

求最小代价。

题解:

按照走过的边数,建原图的分层图。

对每个时刻的每个点,向下一时刻的同一个点连边,表示不走。

然后 $d\cdot a^2$,将 $a^2$ 拆成 $1+3+5+7+\cdots$ 也是常规操作。

最多一共会有 $(n+k)$ 个时刻,需要建 $n(n+k)+2$ 个点。

对于一条边,拆成 $2k(n+k)$ 条边。

然后跑费用流即可。

这里要注意加边时尽可能把价值小的边放前面,这样费用流常数比较小,否则可能被卡常。

Code:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=6000,inf=1e9;
struct edge{
    int to,nxt,cap,cost;
}e[(int)1e7];
int n,cnt=1,head[N],T,iter[N],m,k,c,d,a[N],TT[55][120],nod;
int dist[N],ans;
bool vis[N];
int q[1234567]; 
inline void addedge(int u,int v,int cost,int cap){
    e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
}
int dfs(int u,int f){
    if(u==T||!f)return f;
    vis[u]=1;
    int ret=0;
    for(int&i=iter[u];i;i=e[i].nxt)
    if(e[i].cap&&!vis[e[i].to]&&dist[e[i].to]==dist[u]+e[i].cost){
        int d=dfs(e[i].to,min(f,e[i].cap));
        e[i].cap-=d,e[i^1].cap+=d,f-=d,ret+=d;
        if(!f)break;
    }
    vis[u]=0;
    return ret;
}
bool MCMF(){
    for(int i=1;i<=T;++i)dist[i]=1e9+1;
    int h=1,t=1;q[1]=0;
    for(;h<=t;){
        int u=q[h++];
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].cap&&dist[e[i].to]>dist[u]+e[i].cost){
            dist[e[i].to]=dist[u]+e[i].cost;
            if(!vis[e[i].to])
            vis[e[i].to]=1,q[++t]=e[i].to;
        }
    }
    if(dist[T]>=1e9)return 0;
    memcpy(iter,head,sizeof(int)*(T+2));
    while(int f=dfs(0,inf))ans+=dist[T]*f;
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>k>>c>>d;
    for(int i=1;i<=n;++i)
    for(int t=0;t<=101;++t)TT[i][t]=++nod;
    T=nod+5;
    for(int i=1;i<=k;++i)cin>>a[i],addedge(0,TT[a[i]][0],0,1);
    for(int t=1;t<=101;++t){
        for(int i=1;i<=n;++i)addedge(TT[i][t-1],TT[i][t],c,k);
    }
    while(m--){
        int u,v;
        cin>>u>>v;
        for(int t=0;t<101;++t)
        for(int i=1;i<=k;++i)addedge(TT[u][t],TT[v][t+1],c+d*(i*i-(i-1)*(i-1)),1),addedge(TT[v][t],TT[u][t+1],c+d*(i*i-(i-1)*(i-1)),1);
    }
    for(int t=0;t<=101;++t)addedge(TT[1][t],T,0,k);
    while(MCMF());
    cout<<ans<<'\n';
    return 0;
}