bzoj3196/洛谷P3380 线段树套splay 【附赠数据生成器】

假标题:震惊!一只蒟蒻两天刷新两次最长代码记录,这究竟是数据结构的扭曲还是代码能力的沦丧?

题目分析

线段树,套splay嘛。如题所言,我们就弄一棵线段树,然后线段树的每一个节点都是一棵splay,这样就方便完成各种操作。
那么这道题(应该)唯一要讲解的就是第二个操作啦,第二个操作就是二分答案+第一个操作。其他的让代码来解答吧。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=50005,N1=N*80,inf=INT_MAX;
int n,m,cur,lim1,lim2;
int a[N],b[N],c[N],rt[N<<2];//rt:splay的根
int son[N1][2],f[N1],sz[N1],v[N1];
stack<int> st;//垃圾回收
//以下是splay部分
int newjd() {//开一个新的splay节点
    if(st.empty()) return ++cur;
    else {int re=st.top();st.pop();return re;}
}
void up(int x) {sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
int is(int x) {return son[f[x]][1]==x;}
void spin(int x,int &mb) {
    int fa=f[x],g=f[fa],t=is(x);
    if(fa!=mb) son[g][is(fa)]=x;
    else mb=x;
    f[fa]=x,f[x]=g,f[son[x][t^1]]=fa;
    son[fa][t]=son[x][t^1],son[x][t^1]=fa;
    up(fa),up(x);
}
void splay(int x,int &mb) {
    while(x!=mb) {
        if(f[x]!=mb) {
            if(is(x)^is(f[x])) spin(x,mb);
            else spin(f[x],mb);
        }
        spin(x,mb);
    }
}
int sbuild(int l,int r,int fa) {//新建一棵splay
    if(l>r) return 0;
    int x=newjd(),mid=(l+r)>>1;
    if(l!=r) son[x][0]=sbuild(l,mid-1,x),son[x][1]=sbuild(mid+1,r,x);
    f[x]=fa,v[x]=a[mid],up(x);
    return x;
}
int sask(int x,int num) {
    int re=0;
    while(x)
        if(v[x]<num) re+=sz[son[x][0]]+1,x=son[x][1];
        else x=son[x][0];
    return re-1;//有哨兵节点
}
int find(int x,int num) {//寻找v值为num的节点
    if(v[x]==num) return x;
    if(v[x]>num) return find(son[x][0],num);
    return find(son[x][1],num);
}
void del(int i,int num) {//删除以rt[i]为根的splay中一个v值为num的节点
    int x=find(rt[i],num); splay(x,rt[i]);
    if(son[x][0]*son[x][1]==0) rt[i]=son[x][0]+son[x][1];
    else {
        int y=son[x][1];
        while(son[y][0]) y=son[y][0];
        f[son[x][0]]=y,son[y][0]=son[x][0],rt[i]=son[x][1];
        while(y) up(y),y=f[y];
    }
    son[x][0]=son[x][1]=f[x]=v[x]=sz[x]=0,st.push(x),f[rt[i]]=0;
}
void ins(int &x,int num,int pre) {
    if(!x) {x=newjd(),v[x]=num,f[x]=pre,sz[x]=1;return;}
    if(num<=v[x]) ins(son[x][0],num,x);
    else ins(son[x][1],num,x);
    up(x);
}
int spre(int x,int num) {
    if(!x) return  -inf;
    if(num<=v[x]) return spre(son[x][0],num);
    return max(spre(son[x][1],num),v[x]);
}
int snxt(int x,int num) {
    if(!x) return inf;
    if(num>=v[x]) return snxt(son[x][1],num);
    return min(snxt(son[x][0],num),v[x]);
}
//以下是线段树部分
void build(int s,int t,int i) {//建树
    if(s==t) {
        int t1=a[s-1],t2=a[t+1];//添加哨兵节点
        a[s-1]=-inf,a[t+1]=inf;
        rt[i]=sbuild(s-1,t+1,0);
        a[s-1]=t1,a[t+1]=t2;return;
    }
    int mid=(s+t)>>1;
    build(s,mid,i<<1),build(mid+1,t,(i<<1)|1);
    //以下是归并排序
    int k1=s,k2=mid+1,k3=0,t1=a[s-1],t2=a[t+1];
    a[s-1]=-inf,a[t+1]=inf;
    while(k1<=mid&&k2<=t)
        if(a[k1]<=a[k2]) b[++k3]=a[k1],++k1;
        else b[++k3]=a[k2],++k2;
    while(k1<=mid) b[++k3]=a[k1],++k1;
    while(k2<=t) b[++k3]=a[k2],++k2;
    for(int j=1;j<=k3;++j) a[s+j-1]=b[j];
    rt[i]=sbuild(s-1,t+1,0),a[s-1]=t1,a[t+1]=t2;
}
int ask(int l,int r,int s,int t,int i,int num) {
    if(l<=s&&t<=r) return sask(rt[i],num);
    int mid=(s+t)>>1,re=0;
    if(l<=mid) re=ask(l,r,s,mid,i<<1,num);
    if(mid+1<=r) re+=ask(l,r,mid+1,t,(i<<1)|1,num);
    return re;
}
void chan(int x,int s,int t,int i,int num) {
    del(i,c[x]),ins(rt[i],num,0);
    if(s==t) {c[x]=num;return;}
    int mid=(s+t)>>1;
    if(x<=mid) chan(x,s,mid,i<<1,num);
    else chan(x,mid+1,t,(i<<1)|1,num);
}
int pre(int l,int r,int s,int t,int i,int num) {
    if(l<=s&&t<=r) return spre(rt[i],num);
    int mid=(s+t)>>1,re=-inf;
    if(l<=mid) re=pre(l,r,s,mid,i<<1,num);
    if(mid+1<=r) re=max(re,pre(l,r,mid+1,t,(i<<1)|1,num));
    return re;
}
int nxt(int l,int r,int s,int t,int i,int num) {
    if(l<=s&&t<=r) return snxt(rt[i],num);
    int mid=(s+t)>>1,re=inf;
    if(l<=mid) re=nxt(l,r,s,mid,i<<1,num);
    if(mid+1<=r) re=min(re,nxt(l,r,mid+1,t,(i<<1)|1,num));
    return re;
}
//二分答案
void work(int x,int y,int z) {
    int l=lim1,r=lim2,mid,re;
    while(l<=r) {
        mid=(l+r)>>1;
        if(ask(x,y,1,n,1,mid)>=z) r=mid-1;
        else re=mid,l=mid+1;
    }
    printf("%d\n",re);
}
int main()
{
    int bj,x,y,z;
    scanf("%d%d",&n,&m),lim1=inf,lim2=-inf;
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]),lim1=min(lim1,a[i]),lim2=max(lim2,a[i]),c[i]=a[i];
    build(1,n,1);
    while(m--) {
        scanf("%d%d%d",&bj,&x,&y);
        if(bj==1) scanf("%d",&z),printf("%d\n",ask(x,y,1,n,1,z)+1);
        else if(bj==2) scanf("%d",&z),work(x,y,z);
        else if(bj==3) lim1=min(lim1,y),lim2=max(lim2,y),chan(x,1,n,1,y);
        else if(bj==4) scanf("%d",&z),printf("%d\n",pre(x,y,1,n,1,z));
        else if(bj==5) scanf("%d",&z),printf("%d\n",nxt(x,y,1,n,1,z));
    }
    return 0;
}

数据生成器

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
    srand(time(0));
    n=rand()%10+1,m=rand()%10+1,printf("%d %d\n",n,m);
    for(int i=1;i<=n;++i) printf("%d ",rand()%100);
    puts("");
    for(int i=1;i<=m;++i) {
        int bj=rand()%5+1;
        printf("%d ",bj);
        if(bj==3) printf("%d %d\n",rand()%n+1,rand()%100);
        else {
            int r=rand()%n+1,l=rand()%r+1;
            printf("%d %d ",l,r);
            if(bj==2) printf("%d\n",rand()%(r-l+1)+1);
            else printf("%d\n",rand()%100);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/78986835
今日推荐