P12508

P12508 程序员的日常

题意:

给定排列 \(a\) 。将序列按顺序切成 \(k\) 个非空连续段,每段的权值为该段最大值。令切分后的权值序列为 \(b\) 。对每个询问 \((k,i)\),求所有 \(k\) 段切分中,使 \(b\) 字典序最小的方案下,\(b_i\)​ 的值。

\(n,q\le3\times 10^5\)

做法:

固定 \(k\) :

假设先固定一个 \(k\) ,如何令字典序最小呢?

这里有一个显然的贪心策略:每次在保证当前段的最大值不会更大的同时,让下一段的第一个数最小。

如:

\[6,4,2,3,1,7,5 \]

在 \(k=5\) 的情况下,可以划分成:

\[[6,4][2,3][1][7][5] \]

注意第一段不能划成:

\[[6,4,2,3] \]

因为这样后面就不够分了。

如果能够使得后面够分且能够保证当前端的最大值不会更大,则称这个区间时满足条件的。

但实际上这样有反例:

如:

\[4,1,3,2 \]

在 \(k=2\) 的情况下,按照上面策略会划分成:

\[[4][1,3,2] \]

但最优策略为:

\[[4,1,3][2] \]

划错的原因是因为忽略了最后一段必须要取完,所以当最后一步需要改变策略:

保证当前段的最大值不会更大时,让下一个起点的后缀最大值最小。

这一步也是简单的。

这就是一个 \(O(qk)\) 的时间复杂度。

扩展到一般情况:

用跳跃的视角来看,后面思考起来更为简单。

如当 \(k=5\) 时,可用如图的跳跃表示。

当 \(k=4\) 时则是这样:

回顾之前贪心的过程,某一步相当于是向后跳到满足条件的区间的最小值的位置。

当 \(k\) 从 \(i\) 变为 \(i-1\) 时,某一步跳跃到的点可能会发生改变。我们可以从前往后检查。

设当前点是 \(x\) ,假设跳到的点从 \(y\) 变成了 \(y'\) 。

那么显然当 \(k=i\) 时,如果选了 \(y'\) 后面就一定会出现不够选的情况,不然不可能不选 \(y'\) 。

所以当 \(k=i-1\) 时, 选了 \(y'\) 则后面刚好够选,所以后面每一段长度都为 \(1\) 。

记 \(nxtl[i]\) 表示 \(i\) 后面满足条件的区间中第一个小于 \(a[i]\) 的数的位置 , \(nxtg[i]\) 表示 \(i\) 后面满足条件的区间中第一个大于 \(a[i]\) 的数的位置。

如果 \(y'\) 要比 \(y\) 更优,那么就要有 \(a[y']<a[y]\) 。所以检查 \(x\) 是否能够跳到 \(nxtl[y]\) , 如果能跳到则更新当前的区间。

但是从前往后挨个检查,时间复杂度显然是 \(O(n^2)\) 级别,根本无法接受。

所以考虑用数据结构快速维护当前选跳到的点。

考虑维护一个数组 \(ins[i]\) 表示第 \(i\) 个点是否在当前的跳跃路径上。

然后还要维护一个数组 \(t[i]\) 表示第 \(i\) 个点再过 \(t[i]\) 轮其跳到的终点才会改变。(如果 \(ins[i]=0\) 则 \(t[i]=∞\))

如:上述例子中

起初(\(k=n\) 时)有:

\[ins:1,1,1,1,1,1,1 \]

\[t:1,2,∞,∞ ,∞ ,∞ ,∞ \]

无穷表示目前状态下永远不会改变。

由于第一个数 \(6\) 最开始只能跳到 \(4\) ,但是当 \(k-1\) 后,\(6\) 就能够跳到 \(2\) ,此时 \(6\) 跳到的下一个数改变,所以 \(t[1]=1\) 。

每一轮令 \(t\) 全局减一,那么 \(t\) 中值为 \(0\) 的位置,就是会改变的位置。

根据上文描述,我们找到第一个值为 \(0\) 的位置,然后更新,具体地:

设第一个值为 \(0\) 的位置是 \(x\)。

首先重新计算 \(x\) 当前所在的段数。设其为 \(rk\),然后计算新的路径边界(其实也就是更改后跳到的点):

\[rb=\min(nxtg[x],n-i+rk+1) \]

于是:

  1. 把 \(ins\) 中 \([x+1,rb-1]\) 赋值为 \(0\),\(t\) 中对应区间设为 \(INF\)。

  2. 把 \(ins\) 中 \([rb,n]\) 赋值为 \(1\),\(t\) 中对应区间恢复成初始值。

  3. 重新计算 \(t[x]\):若 \(nxtl[rb]\) 存在且不超过 \(nxtg[x]\),则

    \[t[x]=nxtl[rb]-rb \]

否则令 \(t[x]=INF\)。

离线询问,然后查询是简单的,但要注意一些特判。

Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
const int INF=1e9;
int n,q;
int a[N];
int nxt_ge[N],nxt_le[N];
int sk[N],tp=0;
void init_nxt(){
    tp=0;
    for (int i=1;i<=n;i++){
        while(tp&&a[sk[tp]]<a[i]){
            nxt_ge[sk[tp]]=i;
            tp--;
        }
        sk[++tp]=i;
    }
    while(tp){
        nxt_ge[sk[tp]]=n+1;
        tp--;
    } 
    for (int i=1;i<=n;i++){
        while(tp&&a[sk[tp]]>a[i]){
            nxt_le[sk[tp]]=i;
            tp--;
        }
        sk[++tp]=i;
    }
    while(tp){
        nxt_le[sk[tp]]=n+1;
        tp--;
    } 
}
int res_tim[N];
struct Segment1{
    struct A{
        int cnt,maxn;
        int tag;
    }tree[N*4];

    void push_up(int k){
        tree[k].cnt=tree[k*2].cnt+tree[k*2+1].cnt;
        tree[k].maxn=max(tree[k*2].maxn,tree[k*2+1].maxn);
    }
    void Build(int k,int l,int r){
        tree[k].tag=-1;
        if (l==r){
            tree[k].maxn=1;
            tree[k].cnt=1;
            return;
        }
        int mid=(l+r)>>1;
        Build(k*2,l,mid);
        Build(k*2+1,mid+1,r);
        push_up(k);
    }
    void mark(int k,int l,int r,int v){
        tree[k].tag=v;
        tree[k].maxn=v;
        tree[k].cnt=v*(r-l+1);
    }
    void push_down(int k,int l,int r,int mid){
        if (tree[k].tag!=-1){
            mark(k*2,l,mid,tree[k].tag);
            mark(k*2+1,mid+1,r,tree[k].tag);
            tree[k].tag=-1;
        }
    }
    void update(int k,int l,int r,int ql,int qr,int v){
        if (ql>qr) return;
        if (ql<=l&&r<=qr){
            mark(k,l,r,v);
            return;
        }
        int mid=(l+r)>>1;
        push_down(k,l,r,mid);
        if (ql<=mid) update(k*2,l,mid,ql,qr,v);
        if (qr>mid) update(k*2+1,mid+1,r,ql,qr,v);
        push_up(k);
    }
    int query(int k,int l,int r,int p){
        if (l==r) return l;
        int mid=(l+r)>>1;
        push_down(k,l,r,mid);
        if (p<=tree[k*2].cnt) return query(k*2,l,mid,p);
        else return query(k*2+1,mid+1,r,p-tree[k*2].cnt);
    }
    int query_sum(int k,int l,int r,int ql,int qr){
        if (ql>r||qr<l) return 0;
        if (ql<=l&&r<=qr) return tree[k].cnt;
        int mid=(l+r)>>1;
        push_down(k,l,r,mid);
        int res=0;
        if (ql<=mid) res+=query_sum(k*2,l,mid,ql,qr);
        if (qr>mid) res+=query_sum(k*2+1,mid+1,r,ql,qr);
        return res;
    }
    int query_pos(int k,int l,int r,int p){
        if (p>r) return -1;
        if (tree[k].maxn==0) return -1;
        if (l==r) return l;
        int mid=(l+r)>>1;
        ll res=-1;
        if (p<=mid) res=query_pos(k*2,l,mid,p);
        if (res==-1) res=query_pos(k*2+1,mid+1,r,p);
        return res;
    }
}T1;
struct Segment2{
    struct A{
        ll minn,init_val;
        int tag,tag2,tag3;
    }tree[N*4];

    void push_up(int k){
        tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
        tree[k].init_val=min(tree[k*2].init_val,tree[k*2+1].init_val);
    }

    void Build(int k,int l,int r){
        tree[k].tag=0;
        tree[k].tag2=-1;
        tree[k].tag3=0;
        if (l==r){
            tree[k].minn=res_tim[l];
            tree[k].init_val=res_tim[l];
            return;
        }
        int mid=(l+r)>>1;
        Build(k*2,l,mid);
        Build(k*2+1,mid+1,r);
        push_up(k);
    }

    void mark(int k,ll v){
        tree[k].tag+=v;
        tree[k].minn+=v;
    }

    void mark2(int k,int v){
        tree[k].tag=0;
        tree[k].tag2=v;
        tree[k].tag3=0;
        tree[k].minn=v;
    }

    void mark3(int k){
        tree[k].tag=0;
        tree[k].tag2=-1;
        tree[k].tag3=1;
        tree[k].minn=tree[k].init_val;
    }

    void push_down(int k){
        if (tree[k].tag2!=-1){
            mark2(k*2,tree[k].tag2);
            mark2(k*2+1,tree[k].tag2);
            tree[k].tag2=-1;
        }
        if (tree[k].tag3){
            mark3(k*2);
            mark3(k*2+1);
            tree[k].tag3=0;
        }
        if (tree[k].tag){
            mark(k*2,tree[k].tag);
            mark(k*2+1,tree[k].tag);
            tree[k].tag=0;
        }
    }

    void recover(int k,int l,int r,int ql,int qr){
        if (ql>qr) return;
        if (ql<=l&&r<=qr){
            mark3(k);
            return;
        }
        int mid=(l+r)>>1;
        push_down(k);
        if (ql<=mid) recover(k*2,l,mid,ql,qr);
        if (qr>mid) recover(k*2+1,mid+1,r,ql,qr);
        push_up(k);
    }

    void cover(int k,int l,int r,int ql,int qr,ll v){
        if (ql>qr) return;
        if (ql<=l&&r<=qr){
            mark2(k,v);
            return;
        }
        int mid=(l+r)>>1;
        push_down(k);
        if (ql<=mid) cover(k*2,l,mid,ql,qr,v);
        if (qr>mid) cover(k*2+1,mid+1,r,ql,qr,v);
        push_up(k);
    }

    void add(int k,int l,int r,int ql,int qr,ll v){
        if (ql>qr) return;
        if (ql<=l&&r<=qr){
            mark(k,v);
            return;
        }
        int mid=(l+r)>>1;
        push_down(k);
        if (ql<=mid) add(k*2,l,mid,ql,qr,v);
        if (qr>mid) add(k*2+1,mid+1,r,ql,qr,v);
        push_up(k);
    }

    void update(int k,int l,int r,int p,int v){
        if (l==r){
            tree[k].minn=v;
            tree[k].tag=0;
            tree[k].tag2=-1;
            tree[k].tag3=0;
            return;
        }
        int mid=(l+r)>>1;
        push_down(k);
        if (p<=mid) update(k*2,l,mid,p,v);
        else update(k*2+1,mid+1,r,p,v);
        push_up(k);
    }

    int fir_pos(int k,int l,int r){
        if (tree[k].minn!=0) return -1;
        if (l==r) return l;
        int mid=(l+r)>>1;
        push_down(k);
        if (tree[k*2].minn==0) return fir_pos(k*2,l,mid);
        if (tree[k*2+1].minn==0) return fir_pos(k*2+1,mid+1,r);
        return -1;
    }
}T2;
int ans[N];
struct A{
    int x,id;
};
vector <A> ques[N];
int suf[N];
void init_suf(){
    suf[n+1]=0;
    for(int i=n;i>=1;i--){
        suf[i]=max(suf[i+1],a[i]);
    }
}
void solve(){
    for (int i=1;i<n;i++){
        if (nxt_le[i+1]<=n&&nxt_le[i+1]<=nxt_ge[i]) res_tim[i]=nxt_le[i+1]-i-1;
        else res_tim[i]=INF;
    }
    res_tim[n]=INF;
    T1.Build(1,1,n);
    T2.Build(1,1,n);
    for (auto now:ques[n]){
        if (now.x==n){
            if (n==1){
                ans[now.id]=suf[1];
                 continue;
            }
            int pre=T1.query(1,1,n,n-1);
            int rb=min(nxt_ge[pre],n);
            ans[now.id]=suf[rb];
        }
        else{
            int pos=T1.query(1,1,n,now.x);
            ans[now.id]=a[pos];
        }
    }
    for (int i=n-1;i>=1;i--){
        T2.add(1,1,n,1,n,-1);
        int pos=T2.fir_pos(1,1,n);
        if (pos==-1){
            int lst=T1.query(1,1,n,i+1);
            T1.update(1,1,n,lst,lst,0);
            T2.update(1,1,n,lst,INF);
        }
        else{
            int rk=T1.query_sum(1,1,n,1,pos);
            int l=pos+1,r=min(nxt_ge[pos],n-i+rk+1)-1;
            T2.cover(1,1,n,l,r,INF);
            T1.update(1,1,n,l,r,0);
            T1.update(1,1,n,r+1,n,1);
            T2.recover(1,1,n,r+1,n);
            int new_tim;
            if (r<n&&nxt_le[r+1]<=n&&nxt_le[r+1]<=nxt_ge[pos]) new_tim=nxt_le[r+1]-r-1;
            else new_tim=INF;
            T2.update(1,1,n,pos,new_tim);    
        }

        for (auto now:ques[i]){
            if (now.x==i){
                if (i==1){
                    ans[now.id]=suf[1];
                    continue;
                }
                int pre=T1.query(1,1,n,i-1);
                int rb=min(nxt_ge[pre],n);
                ans[now.id]=suf[rb];
                continue;
            }
            int pos=T1.query(1,1,n,now.x);
            ans[now.id]=a[pos];
        }
    }

}

int main(){

    cin >> n >> q;
    for (int i=1;i<=n;i++){
        cin >> a[i];
    }
    init_nxt();
    for (int i=1,k,x;i<=q;i++){
        cin >> k >> x;
        ques[k].push_back({x,i});
    } 
    init_suf();
    solve();
    for(int i=1;i<=q;i++){
        cout << ans[i] << endl;
    }


    return 0;
}



posted @ 2026-11-03 23:29  wlhao  阅读(4)  评论(0)    收藏  举报