主席树模板(区间第k小+可持久化数组+带回溯线段树)

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 200010
using namespace std;
int n,m,cnt;
struct Tree
{
    int l,r,sum;
}T[MAXN*60];
int rank[MAXN],root[MAXN];
struct data
{
    int val,ind;
    bool operator < (const data &a) const
    {
        return val<a.val;
    }
}a[MAXN];
void insert(int &num,int &x,int l,int r)
{
    T[cnt++]=T[x];
    x=cnt-1;
    T[x].sum++;
    if(l==r) return;
    int mid=(l+r)/2;
    if(num<=mid) insert(num,T[x].l,l,mid);
    else insert(num,T[x].r,mid+1,r);
}
int query(int i,int j,int k,int l,int r)
{
    if(l==r) return l;
    int t=T[T[j].l].sum-T[T[i].l].sum;
    int mid=(r+l)/2;
    if(k<=t) return query(T[i].l,T[j].l,k,l,mid);
    else return query(T[i].r,T[j].r,k-t,mid+1,r);
}
int main()
{
    T[0].l=T[0].r=T[0].sum=root[0]=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].val);
        a[i].ind=i;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        rank[a[i].ind]=i;
    }
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        root[i]=root[i-1];
        insert(rank[i],root[i],1,n);
    }
    while(m--)
    {
        int i,j,k;
        scanf("%d%d%d",&i,&j,&k);
        printf("%d
",a[query(root[i-1],root[j],k,1,n)].val);
    }
}
 
区间第K小

第K小没什么说的(其实是我还不太会)

下面是可持久化数组的代码

题目大意:对于一个数组,修改一个值或查询某版本下某位的值(查询也算一个版本)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
struct Tree
{
    int ls,rs;
    int val;
}T[1000010*60];
int cnt;
int a[1000010];
int root[1000010];
void build(int l,int r)
{
     if(l==r) {T[++cnt].val=a[l];return;}
     int mid=(l+r)/2;
     build(l,mid);
     int l1=cnt;
     build(mid+1,r);
     int r1=cnt;
     T[++cnt].ls=l1;T[cnt].rs=r1;
}
/*void check(int x,int l,int r)
{
    if(l>r) return;
    if(l==r) {printf("%d ",T[x].val);return;}
    int mid=(l+r)/2;
    check(T[x].ls,l,mid);
    check(T[x].rs,mid+1,r);
    return;
}这里是判断建树是否正确*/
void ask(int p,int x,int l,int r)
{
    if(l>r) return;
    if(l==r) {printf("%d
",T[p].val);return;}
    int mid=(l+r)/2;
    if(x>mid) ask(T[p].rs,x,mid+1,r);
    else ask(T[p].ls,x,l,mid);
}
void change(int p,int x,int y,int l,int r)
{
    if(l>r) return;
    T[++cnt]=T[p];
    if(l==r) {T[cnt].val=y;return;}
    int mid=(l+r)/2;
    if(x>mid) {T[cnt].rs=cnt+1;change(T[p].rs,x,y,mid+1,r);}
    else {T[cnt].ls=cnt+1;change(T[p].ls,x,y,l,mid);}
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,n);
    root[0]=cnt;
    for(int i=1;i<=m;i++)
    {
        root[i]=cnt+1;
        int a,b;
        scanf("%d%d",&a,&b);
        if(b==1)
        {
            int c,d;
            scanf("%d%d",&c,&d);
            change(root[a],c,d,1,n);
        }
        else
        {
            cnt++;
            int c;
            scanf("%d",&c);
            T[root[i]]=T[root[a]];
            ask(root[i],c,1,n);
        }
    }
}
可持久化数组

 带回溯线段树

回溯是指当前版本与k前相同(两次回溯一个版本相当于不回溯,但版本号加了二,再变成上一个版本需要回溯3个版本)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    int l,r,ls,rs;
    int val,tag;
}T[60*250010];
int A[100010];
int root[100010];
int cnt=0,n,m;
void build(int l,int r)
{
    if(l==r) {T[++cnt].val=A[l];T[cnt].l=l;T[cnt].r=r;return;}
    int mid=(l+r)/2,tmp;
    build(l,mid);
    tmp=cnt;
    build(mid+1,r);
    T[++cnt].ls=tmp;
    T[cnt].rs=cnt-1;
    T[cnt].l=l;T[cnt].r=r;
    T[cnt].val=T[cnt-1].val+T[tmp].val;
}
void pushdown(int id)
{
    if(id)
    {
        T[T[id].ls].tag+=T[id].tag;
        T[T[id].rs].tag+=T[id].tag;
        T[id].val=T[T[id].ls].tag*(T[T[id].ls].r-T[T[id].ls].l)+T[T[id].ls].val;
        T[id].val+=T[T[id].rs].tag*(T[T[id].rs].r-T[T[id].rs].l)+T[T[id].rs].val;
        T[id].tag=0;
    }
    return;
}
void change(int id,int v,int l,int r)
{
    T[++cnt]=T[id];
    if(T[cnt].r==r&&T[cnt].l==l) {T[cnt].tag+=v;return;}
    pushdown(cnt);
    int tmp=cnt;
    int mid=(T[id].l+T[id].r)/2;
    if(mid>=l) {T[tmp].ls=cnt+1;change(T[id].ls,v,l,min(mid,r));}
    if(mid<r) {T[tmp].rs=cnt+1;change(T[id].rs,v,max(mid+1,l),r);}
    return;  
}
int query(int id,int l,int r)
{
    if(T[id].r==r&&T[id].l==l) return T[id].val+T[id].tag*(r-l+1);
    pushdown(id);
    int tmp=0,mid=(T[id].l+T[id].r)/2;
    if(mid>=l) tmp+=query(T[id].ls,l,min(mid,r));
    if(mid<r) tmp+=query(T[id].rs,max(mid+1,l),r);
    return tmp;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&A[i]);
    }
    build(1,n);
    root[0]=cnt;
    int num=0;
    for(int i=1;i<=m;i++)
    {
        int opt;
        scanf("%d",&opt);
        if(opt==1)//区间加
        {
            root[++num]=cnt+1;
            int v,l,r;scanf("%d%d%d",&l,&r,&v);
            change(root[num-1],v,l,r);
        }
        if(opt==2)//区间查询
        {
            int l,r;scanf("%d%d",&l,&r);
            printf("%d
",query(root[num],l,r));
        } 
        if(opt==3)//回溯 
        {
            int k;
            scanf("%d
",&k);
            root[++num]=cnt+1;
            T[++cnt]=T[root[max(0,num-1-k)]];
        }
    }
}
带回溯线段树
原文地址:https://www.cnblogs.com/wjxgy/p/7789327.html