P9247 [集训队互测 2018] 完美的队列 题解

小清晰分块,不卡常\w/。

首先区间push操作时困难的,考虑求出所有操作所有的元素到什么时候全部弹出,求出这个东西后很好统计答案。

考虑分块,散块直接暴力做。整块考虑双指针,等价于有若干元素支持整体加减 \(1\),单点加减 \(1\),统计实时小于等于 \(0\) 的元素个数,这个直接开 \(n\) 个链表就做完啦(感觉这个trick很有前途啊,正在考虑能不能用它出道分块题)。时间空间复杂度都是 \(O(n\sqrt{n})\) 的。代码可能细节比较多,我的实现很丑/kk,建议不看。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 100005
#define B 450
#define CB (N/B+5)
#define pii pair<int,int>
#define fi first
#define se second
int read(){
    int x=0;
    char ch=getchar();
    while(ch<48)ch=getchar();
    while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return x;
}
int n,m,r,a[N],nxt[N],ans[N];
int L[CB],R[CB],bl[N];
vector<int>col[N];
struct query{
    int l,r,x;
    void init(){
        l=read(),r=read(),x=read();
    }
}q[N];
struct queeu{
    int L[N<<2],R[N<<2],len[N<<2],bl[N<<2],cnt,rp;
    void clear(){
        cnt=rp=0;
        for(int i=0;i<=r;i++)
            len[i]=0,L[i]=R[i]=i;
        for(int i=r+1;i<=r+max(n,m);i++)
            L[i]=R[i]=len[i]=0,bl[i]=1;
    }
    int getr(int dep){
        return (dep%r+r)%r;
    }
    void remove(int x){
        x+=r;
        R[L[x]]=R[x],L[R[x]]=L[x],len[getr(bl[x])]--;
    }
    void insert(int dep,int x){
        x+=r;
        if(bl[x]+rp>0&&dep<=0)cnt++;
        if(bl[x]+rp<=0&&dep>0)cnt--;
        bl[x]=dep-rp,dep=getr(dep-rp),len[dep]++;
        R[L[dep]]=x,L[x]=L[dep],R[x]=dep,L[dep]=x;
    }
    void addall(){
        cnt-=len[getr(0-rp)],rp++;
    }
    void suball(){
        cnt+=len[getr(1-rp)],rp--;
    }
}li;
int qryidx(int pos){
    return li.bl[pos+r]+li.rp;
}
void calcB(int o,int i,int f){
    if(q[i].l<=L[o]&&R[o]<=q[i].r)
        f<0?li.suball():li.addall();
    else
        for(int j=max(q[i].l,L[o]);j<=min(q[i].r,R[o]);j++){
            int now=li.bl[j+r]+li.rp;
            li.remove(j),li.insert(now+f,j);
        }
}
queue<pii>Q[N];
vector<int>V;
int sz[N],now[N],all;
void tpush(int o,int x){
    Q[o].push({x,all-now[o]}),sz[o]+=all-now[o]+1;
    while(sz[o]>a[o]){
        pii cur=Q[o].front();
        if(cur.se){
            int minn=min(sz[o]-a[o],cur.se);
            Q[o].front().se-=minn,sz[o]-=minn;
        }
        else{
            nxt[cur.fi]=max(nxt[cur.fi],sz[o]==a[o]+1?x:V[now[o]+1+a[o]-(sz[o]-(all-now[o]+1))] );
            Q[o].pop(),sz[o]--;
        }
    }
    now[o]=all;
}
void work1(int o,int len){
    li.clear();
    for(int i=L[o];i<=R[o];i++)
        li.insert(a[i],i);
    int nx=0;
    while(nx<m&&li.cnt<len)
        calcB(o,++nx,-1);
    for(int i=1;i<=m;i++){
        if(q[i].l>R[o]||L[o]>q[i].r)
            continue;
        calcB(o,i,1);
        while(nx<m&&li.cnt<len)
            calcB(o,++nx,-1);
        if(q[i].l<=L[o]&&R[o]<=q[i].r)
            nxt[i]=max(nxt[i],nx+(li.cnt<len));
    }
}
void work2(int o){
    all=0,V.clear(),V.push_back(0);
    for(int i=1;i<=m;i++){
        if(q[i].l<L[o]&&R[o]<q[i].r)
            all++,V.push_back(i);
        if(bl[q[i].l]!=o&&bl[q[i].r]!=o)
            continue;
        for(int j=max(q[i].l,L[o]);j<=min(q[i].r,R[o]);j++)
            tpush(j,i);
    }
    for(int i=L[o];i<=R[o];i++)
        tpush(i,m+1);
    for(int i=L[o];i<=R[o];i++)
        while(Q[i].size())
            nxt[Q[i].front().fi]=m+1,Q[i].pop();
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        r=max(r,a[i]=read());
        bl[i]=bl[i-1];
        if(i%B==1)R[bl[i]]=i-1,L[++bl[i]]=i;
    }
    R[bl[n]]=n,r+=m;
    for(int i=1;i<=m;i++)
        q[i].init(),nxt[i]=i+1,col[q[i].x].push_back(i);
    for(int i=1;i<=bl[n];i++)
        work1(i,R[i]-L[i]+1),work2(i);
    nxt[m+1]=m+1;
    for(int i=1;i<=100000;i++){
        int l=1,r=1;
        for(auto &&j:col[i]){
            if(r>=j)r=max(r,nxt[j]);
            else ans[l]++,ans[r]--,l=j,r=nxt[j];
        }
        ans[l]++,ans[r]--;
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]+=ans[i-1]);
    return 0;
}

posted @ 2025-05-29 16:43  ddh321  阅读(23)  评论(0)    收藏  举报