埃蒙的时空航道

埃蒙的时空航道

题目链接:http://dutacm.club:7217/codesheaven/problem.php?id=1082

题目大意:有$n$个星球,每个星球有$p_i$个军队,$x$星球上的军队最多可迁移不超过$c$个军队到$y$星球上($x \leq y$).现已知一段时间后各个星球会遇到$s_i$个敌人袭击,问最多能干掉多少敌人.

网络流+优先队列优化dp

翻了入门经典好好看了发网络流,比网上参差不齐的blog好太多了= =,也是总算明白了.

最大流最小割定理:

若将所有顶点分成两个点集$S$和$T$,其中源点$s$在$S$中,汇点$t$在$T$中,那么$S$就被称为点割集.

如果将"起点在$S$中,终点在$T$中"的所有有向边删除,则不存在一条从源点$s$到汇点$t$的路径,将这样一组边的集合称为一个$s-t$割,它的容量定义为$c(S,T)=\sum_{u \in S,v \in T}c(u,v)$,称为割值.

 对于任意的$s-t$流$f$和任意$s-t$割$(S,T)$,有$|f| \leqslant c(S,T)$,特别地,最小割等于最大流.


 

根据题意,很自然可以构造网络流模型:建立一个虚源点,向每个星球连一条容量为$p_i$的有向边,每个编号较小的星球向编号较大的星球连一条容量为$c$的有向边,每个星球向虚汇点连一条容量为$s_i$的有向边.最大流复杂度为$O(n \times E^2)$,显然不合适,转向求最小割.因此题网络较为规则,最小割可用DP求解.

定义状态$dp[i][j]$为前$i$个点构成的网络里,点割集包含$j$个点的最小割.则状态转移方程为:

$dp[i][j]=min(dp[i-1][j-1]+s[i],dp[i-1][j]+p[i]+j \times c)$.

复杂度为$O(n^2)$,仍不足以通过此题.

注意到,设$a_t \notin S$,$S'=\{a_i|a_i \in S$且$i \leq t\}$,$|S|=k$,$|S'|=x$,则此时割值为$A=F+p_i+cx$.

当$a_t$被加入到$S$中时,割值变化为$B=F+s_i+c[n-i-(k-x)]$.于是当割集加入一个节点$a_t$,割值会增加$B-A=s_i-p_i+c(n-i-k)=s_i-p_i+c(n-i)-ck$.

令$w_i=s_i-p_i+c(n-i)$,每次加入使$w_i$最小的点到点割集中,所得的割值一定最小.

故可以用优先队列维护$w_i$的最小值,使得复杂度降为$O(nlgn)$.

代码如下:

 1 #include <iostream>
 2 #include <queue>
 3 #define N 100005
 4 using namespace std;
 5 typedef long long ll;
 6 ll n,c,p[N],s[N],temp,ans;
 7 priority_queue<ll>q;
 8 int main(void){
 9     std::ios::sync_with_stdio(false);
10     cin>>n>>c;
11     for(int i=1;i<=n;++i)cin>>p[i],temp+=p[i],ans=temp;
12     for(int i=1;i<=n;++i)cin>>s[i];
13     for(int i=1;i<=n;++i)q.push(-(s[i]-p[i]+(n-i)*c));
14     for(int i=0;i<n;++i){
15         ll t=q.top();q.pop();
16         temp-=t+i*c;
17         ans=min(ans,temp);
18     }
19     cout<<ans<<"\n";
20 }

 

posted @ 2017-03-09 23:34  barriery  阅读(365)  评论(1编辑  收藏  举报