题目
费用流建出来大概是下面的图(没有写边权的边都是容量
∞
\infty
∞,费用为
0
0
0)

所以这个图有
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
A→D→D′→J的流,直接把
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
A→D→F→G→C′→J的流,选
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
F→G退流的情况,可以少几类讨论。
3
,
4.
3,4.
3,4.都是带有退流的性质,具体就是如果
C
′
→
J
C'\rightarrow J
C′→J被流了,那么我们用
C
C
C配上
C
′
C'
C′然后通过
C
′
→
G
C'\rightarrow G
C′→G退流回去让原来的和
C
′
C'
C′匹配的点
X
X
X另找一个配对点再走一个类似
G
→
E
′
G \rightarrow E'
G→E′的路径,注意如果另外找的配对点是
X
′
X'
X′,我们就要顺便把
F
→
G
F\rightarrow G
F→G退流,以此来避免走
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<bits/stdc++.h>
#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<int,int>
#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);
}
}
该博客介绍了如何解决NOI2019中涉及的序列问题,利用模拟费用流的方法进行求解。文章详细分析了四种可能的流法,包括直接匹配、最大值匹配以及两种退流策略,并强调了在实际实现中避免0环的重要性。作者提到使用优先队列(模拟堆)作为关键数据结构来实现解决方案,并指出在处理过程中,使用集合可能导致无法通过所有测试用例。
655

被折叠的 条评论
为什么被折叠?



