传送门
神题,蒟蒻博主只能写
O
(
n
4
)
O(n^4)
O(n4)暴力
d
p
dp
dp(才不告诉你卡常之后貌似能过
40
p
t
s
40pts
40pts呢)
40
p
t
s
40pts
40pts代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=2e5+5;
int n,a[N],b[N],L,K;
namespace subtask1{
int id[N];
inline void Main(){
ll ans=0;
for(ri i=1;i<=n;++i)id[i]=i;
priority_queue<int>q1,q2;
for(ri i=1;i<=20000;++i){
random_shuffle(id+1,id+n+1);
ll ss=0;
for(ri j=1;j<=L;++j)ss+=a[id[j]]+b[id[j]];
for(ri j=L+1;j<=n;++j)q1.push(a[id[j]]),q2.push(b[id[j]]);
for(ri j=L+1;j<=K;++j)ss+=q1.top()+q2.top(),q1.pop(),q2.pop();
ans=max(ans,ss);
while(q1.size())q1.pop();
while(q2.size())q2.pop();
}
cout<<ans<<'\n';
}
}
namespace subtask2{
ll f[2][151][151][151];
inline bool chkmax(ll&a,const ll&b){return a=a>b?a:b,1;}
inline void Main(){
int t=f[0][0][0][0]=0;
for(ri i=1;i<=n;++i){
t^=1;
for(ri j=max(i+L-n,0);j<=K&&j<=i;++j)for(ri k1=max(j,i+K-n);k1<=K&&k1<=i;++k1)for(ri k2=max(j,i+K-n);k2<=K&&k2<=i;++k2){
f[t][j][k1][k2]=0;
j<i?((k1<i)&&(k2<i)&&chkmax(f[t][j][k1][k2],f[t^1][j][k1][k2]),
(k1<i)&&(k2>0)&&chkmax(f[t][j][k1][k2],f[t^1][j][k1][k2-1]+b[i]),
(k2<i)&&(k1>0)&&chkmax(f[t][j][k1][k2],f[t^1][j][k1-1][k2]+a[i])):0;
(j>0)&&(k1>0)&&(k2>0)&&chkmax(f[t][j][k1][k2],f[t^1][j-1][k1-1][k2-1]+a[i]+b[i]);
}
}
ll ans=0;
for(ri i=L;i<=K;++i)ans=max(ans,f[t][i][K][K]);
cout<<ans<<'\n';
}
}
int main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
for(ri tt=read();tt;--tt){
n=read(),K=read(),L=read();
for(ri i=1;i<=n;++i)a[i]=read();
for(ri i=1;i<=n;++i)b[i]=read();
if(n<=150)subtask2::Main();
}
return 0;
}
现在考虑用费用流解决问题。
我们设第一个数列
1
1
1 ~
n
n
n对应编号为
p
1
p_1
p1 ~
p
n
p_n
pn
第二个数列
1
1
1 ~
n
n
n对应编号为
q
1
q_1
q1 ~
q
n
q_n
qn
那么可以这样连边:
(
s
,
p
i
,
1
,
a
i
)
,
(
q
i
,
t
,
1
,
b
i
)
,
(
p
i
,
q
i
,
1
,
0
)
(s,p_i,1,a_i),(q_i,t,1,b_i),(p_i,q_i,1,0)
(s,pi,1,ai),(qi,t,1,bi),(pi,qi,1,0)
并限制总流量为
K
K
K
这样相当于是强制只能选
K
K
K对下标一样的,跟题意不符。
于是我们新建两个点
a
,
b
a,b
a,b。
然后多连上这些边
(
p
i
,
a
,
1
,
0
)
,
(
b
,
q
i
,
1
,
0
)
,
(
a
,
b
,
K
−
L
,
0
)
(p_i,a,1,0),(b,q_i,1,0),(a,b,K-L,0)
(pi,a,1,0),(b,qi,1,0),(a,b,K−L,0)
最后跑费用流就能拿到一个比较好看的分数。
现在考虑模拟费用流,发现我们要维护 3 3 3类堆,分别表示:
- a / b a/b a/b数列还剩下了那些位置没有被选过。
- 对于 a / b a/b a/b数列,有哪些位置在 b / a b/a b/a数列中已经被选过且在 a / b a/b a/b数列中未被选过。
- 有哪些位置在 a , b a,b a,b中都未被选过。
显然第一类点包含第二,三类点。
然后就可以模拟费用流啦。
每次如果
a
→
b
a\rightarrow b
a→b这条边有流量就先流这条边,否则再讨论:
- 从 a a a中选一个 2 2 2类点,从 b b b中选一个 1 1 1类点。
- 从 b b b中选一个 2 2 2类点,从 a a a中选一个 1 1 1类点。
- 从 a , b a,b a,b中选一对 3 3 3类点。
最后要注意退流的情况
100 p t s 100pts 100pts代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int N=2e5+5;
int n,K,L,a[N],b[N],sta[N],Flow;
ll ans;
struct cmp_a{inline bool operator()(const int&x,const int&y){return a[x]<a[y];}};
struct cmp_b{inline bool operator()(const int&x,const int&y){return b[x]<b[y];}};
struct cmp_ab{inline bool operator()(const int&x,const int&y){return a[x]+b[x]<a[y]+b[y];}};
priority_queue<int,vector<int>,cmp_a>q1,p1;
priority_queue<int,vector<int>,cmp_b>q2,p2;
priority_queue<int,vector<int>,cmp_ab>q;
pii A[N],B[N];
inline void init(){
while(q1.size())q1.pop();
while(p1.size())p1.pop();
while(q2.size())q2.pop();
while(p2.size())p2.pop();
while(q.size())q.pop();
ans=0,Flow=0;
sort(A+1,A+n+1),reverse(A+1,A+n+1);
sort(B+1,B+n+1),reverse(B+1,B+n+1);
for(ri i=1;i<=K-L;++i)ans+=A[i].fi+B[i].fi,sta[A[i].se]|=1,sta[B[i].se]|=2;
}
inline void Pop(){
while(q1.size()&&(sta[q1.top()]&1))q1.pop();
while(q2.size()&&(sta[q2.top()]&2))q2.pop();
while(p1.size()&&(sta[p1.top()]^2))p1.pop();
while(p2.size()&&(sta[p2.top()]^1))p2.pop();
while(q.size()&&sta[q.top()])q.pop();
}
inline void update1(){
int x=q1.top(),y=q2.top();
--Flow;
ans+=a[x]+b[y];
if((sta[x]|=1)^3)p2.push(x);
if((sta[y]|=2)^3)p1.push(y);
Flow+=x^y?(sta[x]==3)+(sta[y]==3):1;
}
inline void update2(){
int x,y,s1=0,s2=0,s3=0,detflow1=0,detflow2=0;
if(p1.size())s1=a[p1.top()]+b[q2.top()];
if(p2.size())s2=a[q1.top()]+b[p2.top()];
if(q.size())s3=a[q.top()]+b[q.top()];
int mx=max(max(s1,s2),s3);
ans+=mx;
if(s1==mx){
x=p1.top(),y=q2.top();
sta[x]|=1,sta[y]|=2;
if(sta[y]==3)++Flow;
else p1.push(y);
return;
}
if(s2==mx){
x=q1.top(),y=p2.top();
sta[x]|=1,sta[y]|=2;
if(sta[x]==3)++Flow;
else p2.push(x);
return;
}
if(s3==mx)x=q.top(),sta[x]=3;
}
int main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
for(ri tt=read();tt;--tt){
n=read(),K=read(),L=read();
for(ri i=1;i<=n;++i)sta[i]=0;
for(ri i=1;i<=n;++i)A[i]=pii(a[i]=read(),i);
for(ri i=1;i<=n;++i)B[i]=pii(b[i]=read(),i);
init();
for(ri i=1;i<=n;++i){
switch(sta[i]){
case 0:{q1.push(i),q2.push(i),q.push(i);break;}
case 1:{q2.push(i),p2.push(i);break;}
case 2:{q1.push(i),p1.push(i);break;}
default:{++Flow;break;}
}
}
while(L--){
Pop();
if(Flow){update1();continue;}
update2();
}
cout<<ans<<'\n';
}
return 0;
}