给定一个 \(n\) 个点 \(m\) 条边的有向图, 每一个点有两个权值 \(a_i\) 和 \(b_i\), 能够以 \(b_i\) 的花费把第 \(i\) 个点的 \(a_i\) 变成 \(0\). 最后每一个点 \(i\) 产生的花费为全部从 \(i\) 出发能经过一条有向边直接到达的点 \(j\) 的 \(a_j\) 的 \(\max\). 最小化这个过程当中的总花费.c++
\(n\le 1000,m\le50000\)网络
一点都不套路的最小割.spa
果真我是不会网络流的.rest
对于每一个点, 若是将它的邻接点按照 \(a_j\) 降序排序的话, 不难发现必然要干掉一个前缀的全部 \(a_j\) 才能让这个点在最后统计的时候产生的花费变小. 可是屡次干掉同一个点不能重复计算花费.code
那么咱们一点都不天然地想到最小割. 先把全部点拆成两个, 一个负责计算最终统计时的花费 (A类点), 一个负责计算被干掉的时候产生的花费 (B类点). 被干掉的时候产生的花费直接连一条流量为 \(b_i\) 的边到 \(t\) 就能够了. 最终统计时的花费先从 \(s\) 连一条 \(\infty\) 边到当前点, 而后按照 \(a_j\) 降序拉出一条链来, 链上的每一个点表明一条边, 权值为这条边到达的点的 \(a_j\). 而后再从链上的每一个点连一条 \(\infty\) 边到 \(j\) 对应的点. 这样的话若是 \(s\verb|-|t\) 被割断, 那么对于每个 A 类点, 后面必然是割掉了某个 \(a_j\), 同时全部大于被割断的 \(a_j\) 的边邻接的点必然都已经被割掉了 \(b_i\).blog
建图Dinic就能够了.排序
这个拉链而后最小割的套路依然没有学会...果真我仍是太菜了QAQ...get
什么你问我 \(n+m\) 个点Dinic怎么跑过去的? 我怎么知道?Dinic的运行速度大概都是靠信仰吧...it
恋恋世界第一!ast
#include <bits/stdc++.h> const int MAXV=1e5+10; const int MAXE=5e6+10; const int INF=0x7FFFFFFF; struct Edge{ int from; int to; int flow; Edge* rev; Edge* next; }; Edge E[MAXE]; Edge* head[MAXV]; Edge* cur[MAXV]; Edge* top=E; int v; int e; int a[1010]; int b[1010]; int depth[MAXV]; std::vector<int> link[1010]; bool BFS(int,int); int Dinic(int,int); int DFS(int,int,int); void Insert(int,int,int); int main(){ freopen("magic.in","r",stdin); freopen("magic.out","w",stdout); scanf("%d%d",&v,&e); for(int i=1;i<=v;i++) scanf("%d",a+i); for(int i=1;i<=v;i++) scanf("%d",b+i); for(int i=0;i<e;i++){ int a,b; scanf("%d%d",&a,&b); link[a].push_back(b); } for(int i=1;i<=v;i++) std::sort(link[i].begin(),link[i].end(),[](int a,int b){return ::a[a]>::a[b];}); int s=0,t=1,cnt=v*2+1; for(int i=1;i<=v;i++){ Insert(s,i+1,INF); Insert(i+v+1,t,b[i]); int last=i+1; for(size_t j=0;j<link[i].size();j++){ ++cnt; Insert(cnt,v+link[i][j]+1,INF); Insert(last,cnt,a[link[i][j]]); last=cnt; } } printf("%d\n",Dinic(s,t)); return 0; } int Dinic(int s,int t){ int ans=0; while(BFS(s,t)) ans+=DFS(s,INF,t); return ans; } bool BFS(int s,int t){ memset(depth,0,sizeof(depth)); std::queue<int> q; q.push(s); depth[s]=1; cur[s]=head[s]; while(!q.empty()){ s=q.front(); q.pop(); for(Edge* i=head[s];i!=NULL;i=i->next){ if(i->flow>0&&depth[i->to]==0){ depth[i->to]=depth[s]+1; cur[i->to]=head[i->to]; if(i->to==t) return true; q.push(i->to); } } } return false; } int DFS(int s,int flow,int t){ if(s==t||flow<=0) return flow; int rest=flow; for(Edge*& i=cur[s];i!=NULL;i=i->next){ if(i->flow>0&&depth[i->to]==depth[s]+1){ int tmp=DFS(i->to,std::min(rest,i->flow),t); if(tmp<=0) depth[i->to]=0; rest-=tmp; i->flow-=tmp; i->rev->flow+=tmp; if(rest<=0) break; } } return flow-rest; } inline void Insert(int from,int to,int flow){ top->from=from; top->to=to; top->flow=flow; top->rev=top+1; top->next=head[from]; head[from]=top++; top->from=to; top->to=from; top->flow=0; top->rev=top-1; top->next=head[to]; head[to]=top++; }