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);
}