[NOI2019]序列(模拟费用流)

题目
费用流建出来大概是下面的图(没有写边权的边都是容量 ∞ \infty ,费用为 0 0 0
[NOI2019]序列(模拟费用流)_第1张图片
所以这个图有 4 4 4种流法(其实有 5 5 5种,但是第 5 5 5种在实际实现中可以被这 4 4 4种覆盖。)

1. 1. 1.类似于 A → D → D ′ → J A \rightarrow D \rightarrow D' \rightarrow J ADDJ的流,直接把 a i + b i a_i+b_i ai+bi选中。
2. 2. 2.类似于 A → D → F → G → C ′ → J A\rightarrow D \rightarrow F \rightarrow G\rightarrow C'\rightarrow J ADFGCJ的流,选 a i a_i ai中最大和 b i b_i bi中最大的值匹配,显然两边最大值位置相同时 1 1 1 2 2 2要更优秀,此时走 1 1 1不走 2 2 2,尽管我们的费用是一样的,这样写的好处是费用流的流就不会有让你走 0 0 0环来给 F → G F\rightarrow G FG退流的情况,可以少几类讨论。
3 , 4. 3,4. 3,4.都是带有退流的性质,具体就是如果 C ′ → J C'\rightarrow J CJ被流了,那么我们用 C C C配上 C ′ C' C然后通过 C ′ → G C'\rightarrow G CG退流回去让原来的和 C ′ C' C匹配的点 X X X另找一个配对点再走一个类似 G → E ′ G \rightarrow E' GE的路径,注意如果另外找的配对点是 X ′ X' X,我们就要顺便把 F → G F\rightarrow G FG退流,以此来避免走 0 0 0环。
4 4 4 3 3 3 A A A这一侧的类似情况。
然后注意写 s e t set set是怎么卡都过不了的。
写优先队列模拟堆即可。
A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define pii pair
#define mp make_pair
#define LL long long
#define Fi first
#define Se second
#define inf 0x3f3f3f3f
using namespace std;

int n,K,L,a[maxn],b[maxn];
int usa[maxn],usb[maxn];
priority_queue<pii >Ha,Hb,Fa,Fb,G;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}

int main(){
	
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	
	int T;read(T);
	for(;T--;){
		read(n),read(K),read(L);
		for(;!Ha.empty();Ha.pop());
		for(;!Hb.empty();Hb.pop());
		for(;!Fa.empty();Fa.pop());
		for(;!Fb.empty();Fb.pop());
		for(;!G.empty();G.pop());
		rep(i,1,n) usa[i] = usb[i] = 0;
		rep(i,1,n) read(a[i]),Ha.push(mp(a[i],i));
		rep(i,1,n) read(b[i]),Hb.push(mp(b[i],i)),G.push(mp(a[i]+b[i],i));
		LL ans = 0;int stC = K-L;
		rep(Tim,1,K){
			for(;!Ha.empty() && usa[Ha.top().Se];Ha.pop());
			for(;!Hb.empty() && usb[Hb.top().Se];Hb.pop());
			for(;!Fa.empty() && usa[Fa.top().Se];Fa.pop());
			for(;!Fb.empty() && usb[Fb.top().Se];Fb.pop());
			for(;!G.empty() && (usa[G.top().Se] || usb[G.top().Se]);G.pop());
			if(stC){
				int x = (Ha.top()).Se , y = (Hb.top()).Se;
				ans += a[x] + b[y];
				stC--;
				
				usa[x] = 1;
				if(!usb[x]) Fb.push(mp(b[x],x));
				else stC++;
				
				usb[y] = 1;
				if(!usa[y]) Fa.push(mp(a[y],y));
				else stC++;
			}
			else{
				int A = (G.empty() ? -inf : (G.top()).Fi) , B = (Fa.empty() ? -inf : (Fa.top()).Fi) + (Hb.top()).Fi , C = (Ha.top()).Fi + (Fb.empty() ? -inf : (Fb.top()).Fi);
				if(A >= B && A >= C){
					ans += A;
					int t = (G.top()).Se;
					usa[t] = usb[t] = 1;
				}
				else if(B >= A && B >= C){
					ans += B;
					int x = (Fa.top()).Se , y = (Hb.top()).Se;
					if(usa[y]) stC++;
					else Fa.push(mp(a[y],y));
					usa[x] = usb[y] = 1;
				}
				else{
					ans += C;
					int x = (Fb.top()).Se , y = (Ha.top()).Se;
					if(usb[y]) stC++;
					else Fb.push(mp(b[y],y));
					usb[x] = usa[y] = 1;
				}
			}
		}
		printf("%lld\n",ans);
	}
}

你可能感兴趣的:(模拟费用流)