把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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二分
}
posted @ 2021-07-16 11:59  TheLostWeak  阅读(75)  评论(0编辑  收藏  举报