【题解】P9732 [CEOI 2023] Trade
P9732 [CEOI 2023] Trade
简要题意
有长度为 \(n\) 的序列 \(s\) 与 \(c\) 和一个整数 \(k\)。
请选定一个区间 \([l,r] (r-l+1\ge k)\),并选定其中恰好 \(k\) 个下标组成集合 \(A\)。
请你最大化 \(\displaystyle \sum_{x\in A} s_x-\sum_{i=l}^r c_i\),并求出在价值最大化的前提下,有哪些下标能在某种最优方案中被选入集合 \(A\)。
题解
知识点:决策单调性,主席树,双指针。
一个显然的事实,所选定的 \(k\) 个 \(s_i\) 一定是该区间内 \(s_i\) 的前 \(k\) 大。
设当前考虑到右端点为 \(r\) 的区间,设其最优决策点为 \(l'\),当 \(r\) 增大时,思考 \(l'\) 如何变化。
答案是显然的,\(l'\) 一定单调不减,倘若 \(l'\) 在 \(r\) 变大之后突然变小,则说明 \(l'\) 的新位置的 \(s_i\) 会被选入前 \(k\) 大,否则只会产生 \(-c_i\) 的负贡献,既然如此,为什么不在 \(r\) 没增大的时候到这个位置呢?毕竟随着区间左右扩大,前 \(k\) 大是单调不减的。故得证。
所以这题满足决策单调性,且满足四边形不等式。
那第一问就是个分治处理决策单调性的板子了,用主席树可以做到时间复杂度 \(O(n \log^2n)\),来看第二问。
显然,\(i\) 被选入当且仅当 \(s_i\) 大于等于某个最优区间 \([l,r]\) 的第 \(k\) 大。
一个显然的思路是,在转移的过程中处理出所有最有区间,遍历每个区间,标记该区间中 \(s_i\) 大于等于第 \(k\) 大的 \(i\)。
但是最优区间最坏情况下有 \(O(n^2)\),得去除大部分无用的区间。
先考虑两个最优区间 \([a,d],[b,c]\) 满足 \(a<b\le c<d\),由四边形不等式得出 \(w(a,c)+w(b,d)\ge w(a,d)+w(b,c)\),又因为 \(w(a,d)=w(b,c)=ans\),则一定有 \(w(a,c)=w(b,d)=ans\)。
这时候 \([a,d]\) 是无用的,因为任何一个在这个区间里当前 \(k\) 大的 \(s_i\),都能在被它包含的 \([a,c],[b,c],[b,d]\) 取到,而且这些区间更小,在这里的数有更多机会取得前 \(k\) 大。
处理出 \(pre_i\),表示以 \(i\) 为右端点且最靠右(最有机会)的最优决策点(不一定能得到答案),可以证明 \(pre_i\) 随 \(i\) 增大是单调不减的。
所以对于两个相邻且满足 \([pre_i,i],[pre_j,j]\) 能计算出答案的 \(i,j(j<i)\),就如上面所说,枚举 \(k\in [pre_j,pre_i]\) 为左端点(不用枚举 \([j,i]\),因为 \(pre_i\) 是最靠右的,所以一定没有合法的),和 \(i\) 搭配,看是否能计算出答案,如果能就去线段树上区间取 \(\min\)。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 252507
#define int long long
int n,m,c[N],s[N],rt[N],pre[N];
int L=1,R=0,ans=-1e18;
struct hjt{
#define mid ((l+r)>>1)
int cnt=0;
struct node{
int ls,rs,s,c;
}tr[N*32];
inline void ins(int pre,int &k,int l,int r,int d){
k=++cnt;
tr[k]=tr[pre];
tr[k].c++;
tr[k].s+=d;
if(l==r){
return;
}
if(d<=mid){
ins(tr[pre].ls,tr[k].ls,l,mid,d);
}
else{
ins(tr[pre].rs,tr[k].rs,mid+1,r,d);
}
}
inline int ask(int x,int y,int l,int r,int d){
if(l==r){
return min(d,tr[y].c-tr[x].c)*l;
}
int cc=tr[tr[y].rs].c-tr[tr[x].rs].c;
int ss=tr[tr[y].rs].s-tr[tr[x].rs].s;
if(cc<d){
return ask(tr[x].ls,tr[y].ls,l,mid,d-cc)+ss;
}
return ask(tr[x].rs,tr[y].rs,mid+1,r,d);
}
inline int qry(int x,int y,int l,int r,int d){
if(l==r){
return l;
}
int cc=tr[tr[y].rs].c-tr[tr[x].rs].c;
if(cc<d){
return qry(tr[x].ls,tr[y].ls,l,mid,d-cc);
}
return qry(tr[x].rs,tr[y].rs,mid+1,r,d);
}
#undef mid
}t;
struct segt{
#define mid ((l+r)>>1)
int tr[N<<2],mn[N<<2];
inline void build(int k,int l,int r){
tr[k]=mn[k]=1e9+1;
if(l==r){
return;
}
build(k*2,l,mid);
build(k*2+1,mid+1,r);
}
inline void un(int k){
tr[k]=min(tr[k*2],tr[k*2+1]);
}
inline void addt(int k,int d){
mn[k]=min(mn[k],d);
tr[k]=min(tr[k],mn[k]);
}
inline void pd(int k){
if(mn[k]<1e9+1){
addt(k*2,mn[k]);
addt(k*2+1,mn[k]);
mn[k]=1e9+1;
}
}
inline void upd(int L,int R,int k,int l,int r,int d){
if(L<=l&&R>=r){
addt(k,d);
return;
}
pd(k);
if(L<=mid){
upd(L,R,k*2,l,mid,d);
}
if(R>mid){
upd(L,R,k*2+1,mid+1,r,d);
}
un(k);
}
inline int ask(int L,int R,int k,int l,int r){
if(L<=l&&R>=r){
return tr[k];
}
pd(k);
int ans=1e9+1;
if(L<=mid){
ans=ask(L,R,k*2,l,mid);
}
if(R>mid){
ans=min(ans,ask(L,R,k*2+1,mid+1,r));
}
return ans;
}
#undef mid
}b;
inline int calc(int i,int mid){
int now=t.ask(rt[i-1],rt[mid],1,1e9+1,m);
int sc=c[mid]-c[i-1];
return now-sc;
}
inline void sol(int l,int r,int ql,int qr){
if(l>r){
return;
}
pr be={-1e18,0};
int mid=(l+r)>>1;
rep(i,ql,min(mid-m+1,qr)){
int res=calc(i,mid);
be=max(be,{res,i});
}
// cout<<mid<<" from "<<be.se<<' '<<be.fi<<"\n";
pre[mid]=be.se;
ans=max(ans,be.fi);
sol(l,mid-1,ql,be.se);
sol(mid+1,r,be.se,qr);
}
signed main(){
// freopen("Trade.in","r",stdin);
// freopen("Trade.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n){
cin>>c[i];
c[i]+=c[i-1];
}
rep(i,1,n){
cin>>s[i];
t.ins(rt[i-1],rt[i],1,1e9+1,s[i]);
}
sol(m,n,1,n);
cout<<ans<<"\n";
b.build(1,1,n);
pre[0]=1;
int i=m,j=0;
while(i<=n){
if(calc(pre[i],i)==ans){
b.upd(pre[i],i,1,1,n,t.qry(rt[pre[i]-1],rt[i],1,1e9+1,m));
rep(k,pre[j],pre[i]-1){
if(calc(k,i)==ans){
b.upd(k,i,1,1,n,t.qry(rt[k-1],rt[i],1,1e9+1,m));
}
}
j=i;
}
i++;
}
rep(i,1,n){
cout<<(s[i]>=b.ask(i,i,1,1,n));
}
return 0;
}

浙公网安备 33010602011771号