【题解】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;
}
posted @ 2025-07-11 14:53  Lucyna_Kushinada  阅读(11)  评论(0)    收藏  举报