bzoj4358 permu 回滚莫队

题目链接:传送门

序列上的问题?想到莫队。
然而这题像bzoj4241 历史研究一样毒瘤,资瓷 O ( 1 ) O(1) O(1)加入,不资瓷 O ( 1 ) O(1) O(1)删除。
安利一波题解:bzoj4241 历史研究qwq
所以像4241一样,需要用到回滚莫队。
这里开两个数组 l , r l,r l,r l [ i ] l[i] l[i]表示加入i时 i i i到值域连续段左端有多少个数, r [ i ] r[i] r[i]表示加入i时, i i i到值域连续段右端有多少个数。
加入一个数 x x x时,除了 l [ x ] , r [ x ] l[x],r[x] l[x],r[x]要修改,还要修改 x x x所在的值域连续段的左端和右端(以保证下一次更新不会出错)
然后就像回滚莫队的普通操作一样,对于每个块,求解左端点在块中的询问的答案,询问完后撤销左端点的操作。
因为这里加入一个数 x x x时不只更新了 l [ x ] l[x] l[x] r [ x ] r[x] r[x],所以要开一个栈保存更新了哪些位置和更新前的值。
然后撤销就大力 p o p pop pop就珂以了qwq。

毒瘤代码

P s . Ps. Ps.这段代码在 b z o j bzoj bzoj上过了,本地也跑过了所有数据,但是在毒瘤 c z h czh czh O J OJ OJ上就莫名爆 0 0 0 qwq
大概是 r p rp rp又不够了,感觉可以从孟孙那里吸一些 r p rp rp

#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
    re x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=10*x+ch-'0';
        ch=getchar();
    }
    return x*f;
}
namespace I_Love {
 
const int Size=200005;
int n,m,a[Size],belong[Size];
struct Query {
    int id,l,r;
} Q[Size];
inline bool comp(Query x,Query y) {
    if(belong[x.l]!=belong[y.l])    return belong[x.l]<belong[y.l];
    return x.r<y.r;
}
int tmpl[Size],tmpr[Size],tp,stkl[Size],stkr[Size];
int Baoli(int l,int r) {
    int nowmax=0;
    for(re i=l; i<=r; i++) {
        tmpr[a[i]]=tmpr[a[i]+1]+1;
        tmpl[a[i]]=tmpl[a[i]-1]+1;
        int now=tmpl[a[i]]+tmpr[a[i]]-1;
        if(now>nowmax) {
            nowmax=now;
        }
        //要更新a[i]所在的值域连续段的左右端点
        tmpl[a[i]+tmpr[a[i]]-1]=now;
        tmpr[a[i]-tmpl[a[i]]+1]=now;
        //加到栈中
        stkl[++tp]=a[i]+tmpr[a[i]]-1;
        stkr[tp]=a[i]-tmpl[a[i]]+1;
        stkl[++tp]=a[i];
        stkr[tp]=a[i];
    }
    while(tp) {
        tmpl[stkl[tp]]=tmpr[stkr[tp]]=0;
        tp--;
    }
    return nowmax;
}
int lpos[Size],rpos[Size];
int ans,out[Size];
struct Stack {
    int top;
    int posL[Size],valL[Size];
    int posR[Size],valR[Size];
    inline void push_back(int x) {
        top++;
        posL[top]=x+rpos[x]-1;
        valL[top]=lpos[posL[top]];
        posR[top]=x-lpos[x]+1;
        valR[top]=rpos[posR[top]];
    }
} S;
void add(int x,bool flag) {
	//flag表示要不要把当前修改过的东西放入栈里面以便之后撤销 
    lpos[x]=lpos[x-1]+1;
    rpos[x]=rpos[x+1]+1;
    int now=lpos[x]+rpos[x]-1;
    if(now>ans) {
        ans=now;
    }
    if(flag) {
        S.push_back(x);
    }
    lpos[x+rpos[x]-1]=now;
    rpos[x-lpos[x]+1]=now;
}
void del() {
    int top=S.top;
    lpos[S.posL[top]]=S.valL[top];
    rpos[S.posR[top]]=S.valR[top];
    S.top--;
}
void Fujibayashi_Ryou() {
    n=read();
    m=read();
    int siz=sqrt(n);
    for(re i=1; i<=n; i++) {
        a[i]=read();
        belong[i]=(i-1)/siz+1;
    }
    for(re i=1; i<=m; i++) {
        Q[i].l=read();
        Q[i].r=read();
        Q[i].id=i;
    }
    sort(Q+1,Q+1+m,comp);
    int num=belong[n],now=0;
    for(re i=1; i<=num; i++) {
        memset(lpos,0,sizeof(lpos));
        memset(rpos,0,sizeof(rpos));
        int blockR=min(siz*i,n);
        int l=blockR+1,r=blockR;
        ans=0;
        for(re j=now+1; belong[Q[j].l]==i; j++) {
            if(i==belong[Q[j].r]) {
            	//左右端点在一个块里就暴力 
                out[Q[j].id]=Baoli(Q[j].l,Q[j].r);
                now=j;
                continue;
            }
            //右边的操作是不用撤销的 
            while(r<Q[j].r)	add(a[++r],0);
            int pre=ans;
            while(l>Q[j].l)	add(a[--l],1);
            out[Q[j].id]=ans;
            while(S.top)	del();
            while(l<=blockR)
				lpos[a[l]]=rpos[a[l++]]=0;
            ans=pre;
            now=j;
        }
    }
    for(re i=1; i<=m; i++) {
        printf("%d\n",out[i]);
    }
}
 
}
int main() {
    I_Love::Fujibayashi_Ryou();
    return 0;
}
/*
9 3
2 4 3 6 1 5 8 9 7
3 5
2 6
1 4
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值