【CF802O】April Fools' Problem (hard)(WQS二分+模拟费用流)
- 给定两个长度为\(n\)的序列\(a,b\)。
- 要求选出两个长度为\(k\)的子序列\(a_{i_1},a_{i_2},...,a_{i_k}\)和\(b_{j_1},b_{j_2},...,b_{j_k}\),满足\(\forall p\in[1,k],i_p\le j_p\)。
- 求选出子序列权值和的最小值。
- \(k\le n\le 5\times10^5\)
暴力费用流
考虑一个暴力的费用流:
- 从超级源向所有\(a_i\)连一条容量为\(1\)、费用为\(a_i\)的边。
- 从每个\(a_i\)向满足\(i\le j\)的所有\(b_j\)连一条容量为\(1\)、费用为\(0\)的边。
- 从所有\(b_j\)向超级汇连一条容量为\(1\)、费用为\(b_j\)的边。
由于费用流可以一点一点地增加流量,只要増广\(k\)次即可求出答案。
\(WQS\)二分+模拟费用流
注意到费用流每增加一点流量,所需的费用肯定是单调不减的。
也就是说这是一个凸函数,那么就可以使用\(WQS\)二分来优化。
我们二分一个额外价值\(mid\)(\(mid<0\)),把所有\(a_i,b_j\)变成\(a_i+mid,b_j+mid\)。
此时就可以模拟费用流了,每个\(a_i\)只能无脑入堆,而每个\(b_j\)可以选择与一个\(a_i\)配对或是替换掉先前某个\(b_j'\)(注意后者不会使配对数加\(1\),需要在堆中记录元素类型)。
代码:\(O(nlognlogV)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 500000
#define LL long long
using namespace std;
int n,k,a[N+5],b[N+5];LL ans;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
typedef pair<LL,int> Pr;priority_queue<Pr,vector<Pr>,greater<Pr> > Q;I bool Check(Cn LL& x)//模拟费用流
{
RI i,t=ans=0;W(!Q.empty()) Q.pop();for(i=1;i<=n;++i)
{
if(Q.push(make_pair(a[i],-1)),Q.top().first+b[i]+x>=0) continue;//如果价值非负,无法产生贡献
ans+=Q.top().first+b[i]+x,t-=Q.top().second,Q.pop(),Q.push(make_pair(-(b[i]+x),0));//配对/替换;令当前b入堆方便以后替换
}return t<=k;
}
int main()
{
RI i;for(read(n,k),i=1;i<=n;++i) read(a[i]);for(i=1;i<=n;++i) read(b[i]);
LL l=-2e9,r=0,mid;W(l^r) Check(mid=l+r-1>>1)?r=mid:l=mid+1;return Check(r),printf("%lld\n",ans-r*k),0;//WQS二分
}
待到再迷茫时回头望,所有脚印会发出光芒