题意:有M个战役,N个村庄,每当曹操从第i个村庄抽调k个人去往xi战场,第i个村庄会抽调k个人投奔袁绍去往yi战场,且曹操需要为抽调行为支付k*ci的钱,战役分为三种:
- 极其重要:要求曹操方战场上士兵多于袁绍方
- 重要:要求曹操方战场上士兵不少于袁绍方
- 不重要:无所谓
求曹操为了获胜(所有战役满足以上条件)需要支付的金额最小值,若曹操无法获胜,则输出-1
1<=N<=100000;1<=M<=100000;0<=ci<=100000;
1~30组数据,3000MS
题解:特殊最小费用最大流。
构图是关键,从super源向所有的不重要的点连容量为inf,单位费用为0的边,从所有极其重要的点向super汇连容量为1,单位费用为0的边,而从yi点向xi点连容量为inf,单位费用为ci的边(每个战场上只考虑净人数:曹操方人数-袁绍方人数,则从第i个村庄抽调人手相当于从yi战场上向xi战场上抽调人手)。
但想来直接跑费用流会TLE(O(M(N+M)log(N+M))),注意到这个图中,且每次增广流量最大为1,且中间的边容量全为inf,所以可以直接从源开始做一次单源最短路,ans即源点到所有极其重要点距离之和(有无法到达的点则ans=-1),复杂度O((N+M)log(N+M))
#include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; const long long inf=20000000000; long long ans; const int maxn=200000; int t,n,m,x[maxn],y[maxn],c[maxn],w[maxn]; vector <pair<int,int>> edge[maxn]; long long dis[maxn]; queue<int> dl; bool v[maxn]; void spfa(){ int now,to,fy; memset(v,false,sizeof(v)); while (!dl.empty()) dl.pop(); for (int i=1;i<=m;++i) dis[i]=inf;dis[0]=0; dl.push(0);v[0]=true; while (!dl.empty()){ now=dl.front();dl.pop(); v[now]=false; for (int i=0;i<edge[now].size();++i){ to=edge[now][i].first;fy=edge[now][i].second; if (dis[to]>fy+dis[now]){ dis[to]=fy+dis[now]; if (!v[to]) {dl.push(to);v[to]=true;} } } } } int main(){ scanf("%d",&t); for (int q=1;q<=t;++q){ scanf("%d%d",&n,&m); for (int i=0;i<=n;++i) edge[i].clear(); for (int i=1;i<=n;++i) scanf("%d",&x[i]); for (int i=1;i<=n;++i) scanf("%d",&y[i]); for (int i=1;i<=n;++i) scanf("%d",&w[i]); for (int i=1;i<=n;++i) edge[y[i]].push_back(make_pair(x[i],w[i])); for (int i=1;i<=m;++i) {scanf("%d",&w[i]);if (w[i]==0) edge[0].push_back(make_pair(i,0));} spfa(); ans=0; for (int i=1;i<=m;++i) if (w[i]==2) { if (dis[i]==inf) {ans=-1;break;} else ans+=dis[i]; } printf("Case #%d: %lld\n",q,ans); } return 0; }