Luogu P5470 [NOI2019]序列

神仙的模拟费用流。再次感谢陈指导的倾情指导

首先我们要想到费用流的做法,这里先直接贴陈指导博客的图了:

很容易发现我们加入的(p o p')的边容量为(k-l),那么显然会有至少(l)条边经过了(i o i')的路径

最大费用最大流极为答案

考虑模拟费用流,模拟费用流的本质其实就是对费用流的模拟,因此对于接下来的操作我们都会给出它在上图中对应的流法

我们考虑经过(p o p')的一定是最优的边,因此我们需要先贪心地把这条边流满

具体的,我们可以对每个序列各开一个堆,每次选出还未被选择过的(a)中的最大元素(a_x)和还未被选择过的(b)中的最大元素(b_y),将总剩余流量(k)(p o p')的剩余流量减(1)

(S o S' o x o p o p' o y' o T)

但是如果(b_x)或者(a_y)已经被选择过了,那么我们可以进行修改,让它们不流(p o p')这条任选边,而是改走一般的边,将(p o p')的剩余流量加(1)

(S o S' o x o x' o T)(S o S' o y o y' o T)

(p o p')被流满时,接下来我们有三种选择:

  • 选择一对都未被选过的(a_x,b_x),并选择它们。((S o S' o x o x' o T)
  • 选择一个最大的(a_x),以及一个最大的(a_y)已经被选过的(b_y)。考虑把原来与(a_y)通过(p o p')边链接的(b)(a_x)配对,而(a_y)(b_y)自己走(y o y')边。但是需要注意若(b_x)已经被选过的话,我们需要将(p o p')的剩余流量加(1)并重复之前的操作。((S o S' o x o p o v o v' o T)
  • 选择一个最大的(b_y),以及一个最大的(b_x)已经被选过的(a_x)。做法和原理同上

因此我们现在只需要模拟上面的操作即可,具体的,我们开(5)个堆

分别表示两个序列中所有未选的元素,以及两个序列中未选但对应元素已被选的元素,以及下标相同且都未被选的元素对

总复杂度(O(nlog n))

#include<cstdio>
#include<queue>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,INF=1e9;
struct element
{
	int v,p;
	inline element(CI V=0,CI P=0) { v=V; p=P; }
	friend inline bool operator < (const element& A,const element& B)
	{
		return A.v<B.v;
	}
}x,y; priority_queue <element> A,B,AA,BB,C;
int t,n,k,l,ta[N],tb[N]; bool p[N],q[N]; long long ans;
inline bool PA(CI x)
{
	p[x]=1; if (q[x]) return 1; else return BB.push(element(tb[x],x)),0;
}
inline bool PB(CI x)
{
	q[x]=1; if (p[x]) return 1; else return AA.push(element(ta[x],x)),0;
}
inline int gettop(priority_queue <element>& hp)
{
	if (hp.empty()) return -INF; else return hp.top().v;
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d%d",&n,&k,&l),i=1;i<=n;++i) scanf("%d",&ta[i]),p[i]=0;
		for (i=1;i<=n;++i) scanf("%d",&tb[i]),q[i]=0; while (!C.empty()) C.pop();
		while (!A.empty()) A.pop(); while (!AA.empty()) AA.pop();
		while (!B.empty()) B.pop(); while (!BB.empty()) BB.pop();
		ans=0; int w=k-l; for (i=1;i<=n;++i) A.push(element(ta[i],i)),B.push(element(tb[i],i));
		while (k&&w&&!A.empty())
		{
			x=A.top(); A.pop(); y=B.top(); B.pop(); ans+=ta[x.p]+tb[y.p];
			if (PA(x.p)) ++w; if (PB(y.p)) ++w; --k; --w;
		}
		for (i=1;i<=n;++i) if (!p[i]&&!q[i]) C.push(element(ta[i]+tb[i],i));
		while (k)
		{
			while (!C.empty()&&(p[C.top().p]||q[C.top().p])) C.pop();
			while (!A.empty()&&p[A.top().p]) A.pop(); while (!AA.empty()&&p[AA.top().p]) AA.pop();
			while (!B.empty()&&q[B.top().p]) B.pop(); while (!BB.empty()&&q[BB.top().p]) BB.pop();
			--k; int a=gettop(C),b=gettop(A)+gettop(BB),c=gettop(AA)+gettop(B);
			if (a>=b&&a>=c) x=C.top(),C.pop(),ans+=a,p[x.p]=q[x.p]=1; else
			if (b>=a&&b>=c) x=A.top(),A.pop(),y=BB.top(),BB.pop(),ans+=b,q[y.p]=1,PA(x.p)&&(++w);
			else x=AA.top(),AA.pop(),y=B.top(),B.pop(),ans+=c,p[x.p]=1,PB(y.p)&&(++w);
			while (k&&w)
			{
				while (!A.empty()&&p[A.top().p]) A.pop(); while (!B.empty()&&q[B.top().p]) B.pop();
				x=A.top(); A.pop(); y=B.top(); B.pop(); ans+=ta[x.p]+tb[y.p];
				if (PA(x.p)) ++w; if (PB(y.p)) ++w; --k; --w;
			}
		}
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/13545439.html