传送门
神题,蒟蒻博主只能写 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
#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\rightarrow b a→b这条边有流量就先流这条边,否则再讨论:
最后要注意退流的情况
100 p t s 100pts 100pts代码:
#include
#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;
}