【例题】【Splay】NKOJ2504区间翻转问题

    xiaoxiao2021-12-14  19

    NKOJ2504 区间翻转问题 时间限制 : 10000 MS 空间限制 : 265000 KB

    问题描述 给你一个长度为N的序列{ai}和M个操作 1.查询第k个数的值 2.将第k个数增加d 3.查询一段区间的和 4.查询一段区间的最大值 5.将一段区间镜面翻转(例如序列{1,2,3,4,5,6},将从2到5的区间翻转后得到序列{1,5,4,3,2,6}) 对于除操作2,5以外的操作,输出相应的答案

    输入格式 第一行两个正整数N,M 第二行N个整数,为初始的序列 第三行到底M+2行,每行若干个整数 ·如果第一个数为1,那么后面一个正整数k,表示查询第k个数的值 ·如果第一个数是2,那么后面两个正整数k,d,表示将ak增加d ·如果第一个数为3,那么后面两个正整数l,r,表示查询从al到ar的区间和 ·如果第一个数为4,那么后面两个正整数l,r,表示查询从al到ar的最大值 ·如果第一个数为5,那么后面两个正整数l,r,表示翻转从al到ar的这个区间

    输出格式 除操作2,5外每个操作输出占一行,一个整数,为本次提问的答案

    样例输入 6 8 1 2 3 4 5 6 1 4 3 2 5 4 2 2 5 2 5 3 1 3 5 2 5 2 5 1 4 1 6

    样例输出 4 14 2 10 6

    提示 2<=N<=100000 1<=M<=100000 原序列1<=ai<=1000 每次1<=k<=N,1<=l<=r<=N,1<=d<=1000

    来源 感谢nodgd命题并提供数据

    思路: 维护下标,将每个数的下标存入树中。 (1)、查询第k个数:直接找 (2)、第k个数增加d:直接加 (3)、维护区间和。操作时将x-1为根、y+1为右儿子,则y+1左儿子的sum即为所求 (4)、维护区间最大。操作同(3) (5)、lazy标记。操作同(3),再将y+1左儿子打标记

    (3)、(4)、(5)注意覆盖1或n的情况特殊处理

    #include<cstdio> #include<iostream> using namespace std; const int need=100005; int a[need]; //............................................. inline void in_(int &d) { char t=getchar();bool mark=0; while(t<'0'||t>'9') {if(t=='-') mark=1;t=getchar();} for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';if(mark) d=-d; } inline void out_(int x) { if(x<0) {x=-x;putchar('-');} if(x>=10) out_(x/10); putchar(x+'0'); } //............................................. int tot,root; int fa[need],ls[need],rs[need],si[need],num[need],sum[need],mm[need]; bool lazy[need]; inline void putdown(int x) { lazy[x]=0; swap(ls[x],rs[x]); if(ls[x]) lazy[ls[x]]^=1; if(rs[x]) lazy[rs[x]]^=1; } inline void NBHB(int x) { si[x]=si[ls[x]]+si[rs[x]]+1; sum[x]=sum[ls[x]]+sum[rs[x]]+a[num[x]]; mm[x]=max(a[num[x]],max(mm[ls[x]],mm[rs[x]])); } inline void sr(int x) { int y=fa[x],z=fa[y]; if(lazy[z]) putdown(z); if(lazy[y]) putdown(y); if(lazy[x]) putdown(x); ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z; ls[y]=rs[x]; fa[rs[x]]=y; rs[x]=y; fa[y]=x; NBHB(y),NBHB(x); } inline void sl(int x) { int y=fa[x],z=fa[y]; if(lazy[z]) putdown(z); if(lazy[y]) putdown(y); if(lazy[x]) putdown(x); ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z; rs[y]=ls[x]; fa[ls[x]]=y; ls[x]=y; fa[y]=x; NBHB(y),NBHB(x); } int top,s[need]; void splay(int x) { top=0;s[++top]=x; for(int i=x;fa[i];i=fa[i]) s[++top]=fa[i]; while(top) { if(lazy[s[top]]) putdown(s[top]); top--; } int y; while(y=fa[x]) { if(ls[y]==x) sr(x); else sl(x); } root=x; } void insert(int d) //不用putdown,因为先建树,再操作 { if(tot==0) { num[root=tot=1]=d; sum[1]=mm[1]=a[d]; si[1]=1; ls[1]=rs[1]=fa[1]=0; return ; } tot++; int p=root; while(true) { si[p]++; sum[p]+=a[d]; mm[p]=max(mm[p],a[d]); if(d<num[p]) if(ls[p]) p=ls[p]; else{ls[p]=tot;break;} else if(rs[p]) p=rs[p]; else{rs[p]=tot;break;} } si[tot]=1,sum[tot]=a[tot],fa[tot]=p,mm[tot]=a[d],num[tot]=d; splay(tot); } //............................................. int k,d,l,r; int getk(int k) { int p=root; while(true) { if(lazy[p]) putdown(p); if(ls[p]&&si[ls[p]]>=k) p=ls[p]; else if(k==1||(ls[p]&&si[ls[p]]==k-1)) return p; else {k-=si[ls[p]]+1;p=rs[p];} } } void change(int xx) { int y; while(fa[xx]!=root) { y=fa[xx]; if(lazy[y]) putdown(y); if(lazy[xx]) putdown(xx); if(ls[y]==xx) sr(xx); else sl(xx); } } int get() { if(l>1&&r<tot) { int a=getk(l-1),b=getk(r+1); splay(a); change(b); return ls[b]; } else if(l>1&&r==tot) { int a=getk(l-1); splay(a); return rs[a]; } else if(l==1&&r<tot) { int b=getk(r+1); splay(b); return ls[b]; } else return root; } //............................................. int main() { int n,m;scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) in_(a[i]),insert(i); for(int i=1,b,kk;i<=m;i++) { in_(b); if(b==1) { in_(k); out_(a[num[getk(k)]]),putchar(10); } else if(b==2) { in_(k),in_(b); kk=num[getk(k)]; a[kk]+=b; splay(kk); } else { in_(l),in_(r); kk=get(); if(b==3) out_(sum[kk]),putchar(10); else if(b==4) out_(mm[kk]),putchar(10); else lazy[kk]^=1; } } }
    转载请注明原文地址: https://ju.6miu.com/read-971293.html

    最新回复(0)