埃蒙的时空航道
埃蒙的时空航道
题目链接: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 }