- 给定两个长度为(n)的序列(a,b)。
- 要求选出两个长度为(k)的子序列(a_{i_1},a_{i_2},...,a_{i_k})和(b_{j_1},b_{j_2},...,b_{j_k}),满足(forall pin[1,k],i_ple j_p)。
- 求选出子序列权值和的最小值。
- (kle nle 5 imes10^5)
暴力费用流
考虑一个暴力的费用流:
- 从超级源向所有(a_i)连一条容量为(1)、费用为(a_i)的边。
- 从每个(a_i)向满足(ile 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
",ans-r*k),0;//WQS二分
}