zoukankan      html  css  js  c++  java
  • CF1187G

    CF1187G - Gang Up

    题目大意

    (k)个人在一张无向图上往1走,可以选择在原地不动或者走一条边

    一个人在(x)时间到达目的地的代价是(ccdot x)(c)是常数

    一条边同一时间被(x)个人经过的代价是(x^2cdot d)(d)是常数

    最小化代价


    分析

    无法贪心,无法最短路的题目,那就先试试网络流

    考虑将时间和位置压在一起建立节点,时间(leq n+k)

    $(t,u) ightarrow (t+1,u) $

    ((t,u) ightarrow (t+1,v))

    在原地保持的边代价为(c),流量(infty)

    在两点间移动的代价由于与个数有关,可以建立(k)条边

    每条代价是(d(i^2-(i-1)^2)+c=c+(2i-1)d)

    得到一个(O((n+k)n))点数(O((n+k)mcdot k))边数的图

    然后可以考虑依次将每个人加入流量

    但是实际上,并不需要显式地将所有边连出来跑网络流

    每次加入一个点看做一个带回撤的最短路问题,有效的边只有((n+k)m)

    因此复杂度为(O(kcdot ext{SPFA}((n+k)n,(n+k)m)))

    const int N=110,INF=1e9+10;
    
    int n,m,k,C,D;
    int a[N];
    struct Edge{
    	int to,nxt;
    } e[N<<1];
    int head[N],ecnt=1;
    void AddEdge(int u,int v){
    	e[++ecnt]=(Edge){v,head[u]};
    	head[u]=ecnt;
    }
    
    int dis[N][N],pre[N][N],inq[N][N],G[N][N],W[N][N];
    static queue <Pii> que;
    void Upd(int x,int y,int d,int p){
    	if(dis[x][y]>d) {
    		dis[x][y]=d,pre[x][y]=p;
    		if(!inq[x][y]) inq[x][y]=1,que.push(mp(x,y));
    	}
    }
    
    int cnt[N][N];
    
    int main(){
    	n=rd(),m=rd(),k=rd(),C=rd(),D=rd();
    	rep(i,1,k) a[i]=rd();
    	rep(i,1,m) {
    		int u=rd(),v=rd();
    		AddEdge(u,v),AddEdge(v,u);
    		rep(j,1,n+k) G[j][i*2]=G[j][i*2+1]=1;
    	}
    	int ans=0;
    	rep(_,1,k) {
    		int u=a[_];
    		rep(i,1,n+k) rep(j,1,n) dis[i][j]=INF;
    		dis[1][u]=0,que.push(mp(1,u));
    		int tu=1,ti=-1,mi=1e9;
    		while(!que.empty()) {
    			int t=que.front().first,u=que.front().second; que.pop();
    			inq[t][u]=0;
    			if(u==1 && dis[t][u]<mi) mi=dis[t][u],ti=t,tu=u;
    			if(t>1 && W[t-1][u]) Upd(t-1,u,dis[t][u]-C,1001);
    			if(t<n+k) Upd(t+1,u,dis[t][u]+C,1002);
    			for(int i=head[u];i;i=e[i].nxt) {
    				int v=e[i].to;
    				if(G[t-1][i^1]>1) Upd(t-1,v,dis[t][u]-(G[t-1][i^1]-2)*D-C,-(i^1));
    				if(t<n+k) Upd(t+1,v,dis[t][u]+G[t][i]*D+C,i);
    			}
    		}
    		ans+=mi;
    		while(tu!=u || ti!=1) {
    			int t=pre[ti][tu];
    			if(t==1001) --W[ti++][tu];
    			else if(t==1002) W[--ti][tu]++;
    			else if(t<0) G[ti][-t]-=2,tu=e[-t].to,ti++;
    			else G[ti-1][t]+=2,tu=e[t^1].to,ti--;
    		}
    	}
    	printf("%d
    ",ans);
    }
    
  • 相关阅读:
    mysql热备脚本
    linux系统编程-进程
    15-模块
    14-面向对象3
    13-面向对象2
    12-面向对象1
    02-对比两个文件的差异
    11-面向对象基础
    08-不定长函数
    07-缺省参数
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14756550.html
Copyright © 2011-2022 走看看