*線段樹&&主席樹&&可持久化線段樹

今天是2017/5/23,DCDCBigBig的第十一篇博文

啊。。。昨天真是太累了。。。把樹鏈剖分先傳上來了,用的還是線段樹區間修改。然而我並沒有傳線段樹。。。(所以說那份代碼是py來的。。。)那這篇博文就多放點模板,給大家發發福利吧^_^

線段樹1(單點修改)

//求區間最值
//n個元素,m個詢問
//C x y 代表將第x個元素的值改爲y
//D x y 代表在第x到第y個元素中找最大值 
//X x y 代表在第x到第y個元素中找最小值
//附帶一個print函數,可以遍歷並輸出葉節點 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct tree_node{
    int maxn,minn;
}t[400001];
int n,m,x,y,a[100001];
char ord[5];
void build(int l,int r,int u){
    if(l==r){
        t[u].maxn=a[l];
        t[u].minn=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,u*2);
    build(mid+1,r,u*2+1);
    t[u].maxn=max(t[u*2].maxn,t[u*2+1].maxn);
    t[u].minn=min(t[u*2].minn,t[u*2+1].minn);
}
void print(int l,int r,int u){
    if(l==r){
        printf("%d ",t[u].minn);
        return;
    }
    int mid=(l+r)/2;
    print(l,mid,u*2);
    print(mid+1,r,u*2+1);
}
void updata(int l,int r,int u,int x,int y){
    if(l==r){
        t[u].maxn=y;
        t[u].minn=y;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid)updata(l,mid,u*2,x,y);
    else if(x>mid)updata(mid+1,r,u*2+1,x,y);
    t[u].maxn=max(t[u*2].maxn,t[u*2+1].maxn);
    t[u].minn=min(t[u*2].minn,t[u*2+1].minn);
}
int query_max(int l,int r,int u,int L,int R){
    if(l>=L&&r<=R){
        return t[u].maxn;
    }
    int mid=(l+r)/2,ans=-2147483647;
    if(L<=mid)ans=max(ans,query_max(l,mid,u*2,L,R));
    if(R>mid)ans=max(ans,query_max(mid+1,r,u*2+1,L,R));
    return ans;
}
int query_min(int l,int r,int u,int L,int R){
    if(l>=L&&r<=R){
        return t[u].minn;
    }
    int mid=(l+r)/2,ans=2147483647;
    if(L<=mid)ans=min(ans,query_min(l,mid,u*2,L,R));
    if(R>mid)ans=min(ans,query_min(mid+1,r,u*2+1,L,R));
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    for(int i=1;i<=m;i++){
        scanf("%s",ord);
        if(ord[0]=='P'){
            print(1,n,1);
            printf("\n");
            continue;
        }
        scanf("%d%d",&x,&y);
        if(ord[0]=='C'){
            updata(1,n,1,x,y);
        }
        if(ord[0]=='D'){
            printf("%d\n",query_max(1,n,1,x,y));
        }
        if(ord[0]=='X'){
            printf("%d\n",query_min(1,n,1,x,y));
        }
    }
    return 0;
}

線段樹2(區間修改+lazy標記)

區間加值

ps:懶癌發作,不知道拖了幾天才寫好。。。

//區間求和+區間修改(區間加值版)
//類似地,n個元素、m個詢問 
//Q x y表示詢問xy的和 
//A x y z表示將xy的值全部加上z
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct tree{
    int v,lazy;
}t[400001];
int n,m,x,y,z,a[100001];
char ord[5];
void pd(int u,int l,int r){
    if(t[u].lazy!=0){
        int mid=(l+r)/2;
        t[u*2].lazy+=t[u].lazy;
        t[u*2+1].lazy+=t[u].lazy;
        t[u*2].v+=t[u].lazy*(mid-l+1);
        t[u*2+1].v+=t[u].lazy*(r-mid);
        t[u].lazy=0;
    }
}
void build(int l,int r,int u){
    if(l==r){
        t[u].v=a[l];
        return;
    }
    int mid=(l+r)/2;
    t[u].lazy=0;
    build(l,mid,u*2);
    build(mid+1,r,u*2+1);
    t[u].v=t[u*2].v+t[u*2+1].v;
}
void updata(int l,int r,int u,int L,int R,int val){
    if(L<=l&&r<=R){
        t[u].lazy+=val;
        t[u].v+=val*(r-l+1);
        return;
    }
    pd(u,l,r);
    int mid=(l+r)/2;
    if(L<=mid)updata(l,mid,u*2,L,R,val);
    if(R>mid)updata(mid+1,r,u*2+1,L,R,val);
    t[u].v=t[u*2].v+t[u*2+1].v;
}
int query(int l,int r,int u,int L,int R){
    if(L<=l&&r<=R){
        return t[u].v;
    }
    pd(u,l,r);
    int mid=(l+r)/2,ans=0;
    if(L<=mid)ans+=query(l,mid,u*2,L,R);
    if(R>mid)ans+=query(mid+1,r,u*2+1,L,R);
    t[u].v=t[u*2].v+t[u*2+1].v;
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    for(int i=1;i<=m;i++){
        scanf("%s%d%d",ord,&x,&y);
        if(ord[0]=='A'){
            scanf("%d",&z);
            updata(1,n,1,x,y,z);
        }
        if(ord[0]=='Q'){
            printf("%d\n",query(1,n,1,x,y));
        }
    }
    return 0;
}
/*
5 5
1 2 3 4 5
Q 1 5
A 1 3 5
Q 1 5
A 3 5 -3
Q 1 5
*/

區間改值

//區間求和+區間修改(區間改值版)
//n個元素、m個詢問 
//Q x y表示詢問xy的和 
//C x y z表示將xy的值全部改成z
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct tree{
    int v,lazy;
}t[400001];
int n,m,x,y,z,a[100001];
char ord[5];
void pd(int u,int l,int r){
    if(t[u].lazy!=0){
        int mid=(l+r)/2;
        t[u*2].lazy=t[u].lazy;
        t[u*2+1].lazy=t[u].lazy;
        t[u*2].v=t[u].lazy*(mid-l+1);
        t[u*2+1].v=t[u].lazy*(r-mid);
        t[u].lazy=0;
    }
}
void build(int l,int r,int u){
    if(l==r){
        t[u].v=a[l];
        return;
    }
    int mid=(l+r)/2;
    t[u].lazy=0;
    build(l,mid,u*2);
    build(mid+1,r,u*2+1);
    t[u].v=t[u*2].v+t[u*2+1].v;
}
void updata(int l,int r,int u,int L,int R,int val){
    if(L<=l&&r<=R){
        t[u].lazy=val;
        t[u].v=val*(r-l+1);
        return;
    }
    pd(u,l,r);
    int mid=(l+r)/2;
    if(L<=mid)updata(l,mid,u*2,L,R,val);
    if(R>mid)updata(mid+1,r,u*2+1,L,R,val);
    t[u].v=t[u*2].v+t[u*2+1].v;
}
int query(int l,int r,int u,int L,int R){
    if(L<=l&&r<=R){
        return t[u].v;
    }
    pd(u,l,r);
    int mid=(l+r)/2,ans=0;
    if(L<=mid)ans+=query(l,mid,u*2,L,R);
    if(R>mid)ans+=query(mid+1,r,u*2+1,L,R);
    t[u].v=t[u*2].v+t[u*2+1].v;
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build(1,n,1);
    for(int i=1;i<=m;i++){
        scanf("%s%d%d",ord,&x,&y);
        if(ord[0]=='C'){
            scanf("%d",&z);
            updata(1,n,1,x,y,z);
        }
        if(ord[0]=='Q'){
            printf("%d\n",query(1,n,1,x,y));
        }
    }
    return 0;
}
/*
5 5
1 2 3 4 5
Q 1 5
C 1 3 5
Q 1 5
C 3 5 -3
Q 1 5
*/

可持久化線段樹(單點修改)

//單點修改可持久化線段樹(非主席樹)
//給出n個數,q個詢問
//每次詢問形如 0 k l r 或 1 k i x
//表示詢問第k個版本l到r之間的最大值或把第k個版本的第i個數改爲x
//初始的n個數表示第一個版本 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct node{
    int mx,ls,rs;
}t[2000001];
int n,q,k,l,r,ord,cnt=0,tot=0,rt[100001],num[100001];
void build(int l,int r,int &u){
    if(!u)u=++tot;
    if(l==r){
        t[u].mx=num[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,t[u].ls);
    build(mid+1,r,t[u].rs);
    t[u].mx=max(t[t[u].ls].mx,t[t[u].rs].mx);
}
void ins(int &k,int last,int l,int r,int p,int x){
    t[k=++tot].mx=t[last].mx;
    if(l==r){
        t[k].mx=x;
        return;
    }
    t[k].ls=t[last].ls;
    t[k].rs=t[last].rs;
    int mid=(l+r)/2;
    if(p<=mid)ins(t[k].ls,t[last].ls,l,mid,p,x);
    else ins(t[k].rs,t[last].rs,mid+1,r,p,x);
    t[k].mx=max(t[t[k].ls].mx,t[t[k].rs].mx);
}
int query(int &k,int l,int r,int L,int R){
    if(!k)return 0;
    if(L==l&&r==R){
        return t[k].mx;
    }
    int mid=(l+r)/2;
    if(R<=mid)return query(t[k].ls,l,mid,L,R);
    else if(L>mid)return query(t[k].rs,mid+1,r,L,R);
    else return max(query(t[k].ls,l,mid,L,mid),query(t[k].rs,mid+1,r,mid+1,R));
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&num[i]);
    }
    build(1,n,rt[++cnt]);
    for(int i=1;i<=q;i++){
        scanf("%d%d%d%d",&ord,&k,&l,&r);
        if(ord==1){
            ins(rt[++cnt],rt[k],1,n,l,r);
        }else{
            printf("%d\n",query(rt[k],1,n,l,r));
        }
    }
    return 0;
}

可持久化線段樹(區間修改)

//hdu4348
//標記永久化是真的省空間!!! 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
struct node{
    int ls,rs;
    ll v,lazy;
}t[5000001];
int n,m,k,l,r,v,tot,cnt,num[100001],rt[100001];
char ord[3];
int build(int l,int r){
    int u=++tot;
    t[u].lazy=0;
    if(l==r){
        t[u].v=num[l];
        return u;
    }
    int mid=(l+r)/2;
    t[u].ls=build(l,mid);
    t[u].rs=build(mid+1,r);
    t[u].v=t[t[u].ls].v+t[t[u].rs].v;
    return u;
}
int rebuild(int l,int r,int L,int R,int v,int k){
    int u=++tot;
    t[u].v=t[k].v;
    t[u].ls=t[k].ls;
    t[u].rs=t[k].rs;
    t[u].lazy=t[k].lazy;
    if(l==L&&r==R){
        t[u].lazy+=v;
        t[u].v+=v*(r-l+1);
        return u;
    }
    int mid=(l+r)/2;
    if(R<=mid)t[u].ls=rebuild(l,mid,L,R,v,t[k].ls);
    else if(L>mid)t[u].rs=rebuild(mid+1,r,L,R,v,t[k].rs);
    else{
        t[u].ls=rebuild(l,mid,L,mid,v,t[k].ls);
        t[u].rs=rebuild(mid+1,r,mid+1,R,v,t[k].rs);
    }
    t[u].v=t[t[u].ls].v+t[t[u].rs].v;
    t[u].v+=t[u].lazy*(r-l+1);
    return u;
}
ll query(int l,int r,int u,int L,int R){
    if(l==L&&r==R){
        return t[u].v;
    }
    int mid=(l+r)/2;
    ll ans=0;
    if(L>=l&&R<=r)ans+=t[u].lazy*(R-L+1);
    else if(L<l&&R>=l&&R<=r)ans+=t[u].lazy*(R-l+1);
    else if(R>r&&L>=l&&L<=r)ans+=t[u].lazy*(r-L+1);
    else if(L<l&&R>r)ans+=t[u].lazy*(r-l+1);
    if(R<=mid)ans+=query(l,mid,t[u].ls,L,R);
    else if(L>mid)ans+=query(mid+1,r,t[u].rs,L,R);
    else ans+=query(l,mid,t[u].ls,L,mid)+query(mid+1,r,t[u].rs,mid+1,R);
    return ans;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        tot=cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&num[i]);
        }
        rt[0]=build(1,n);
        for(int i=1;i<=m;i++){
            scanf("%s",ord);
            if(ord[0]=='C'){
                scanf("%d%d%d",&l,&r,&v);
                rt[cnt+1]=rebuild(1,n,l,r,v,rt[cnt]);
                cnt++;
            }else if(ord[0]=='Q'){
                scanf("%d%d",&l,&r);
                printf("%lld\n",query(1,n,rt[cnt],l,r));
            }else if(ord[0]=='H'){
                scanf("%d%d%d",&l,&r,&v);
                printf("%lld\n",query(1,n,rt[v],l,r));
            }else{
                scanf("%d",&v);
                cnt=v;
            }
        }
    }
    return 0;
}

主席樹求區間k大

//Orz hjt
//其實和可持久化線段是還是有點區別的QAQ 
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct node{
    int ls,rs,v;
}t[2000001];
int n,q,k,l,r,pos,tot=0,cnt=0,a[100001],b[100001],rt[100001];
int build(int l,int r){
    int u=++tot;
    t[u].v=0;
    if(l==r)return u;
    int mid=(l+r)/2;
    t[u].ls=build(l,mid);
    t[u].rs=build(mid+1,r);
    return u;
}
int ins(int l,int r,int k,int p){
    int u=++tot;
    t[u].ls=t[k].ls;
    t[u].rs=t[k].rs;
    t[u].v=t[k].v+1;
    if(l==r)return u;
    int mid=(l+r)/2;
    if(p<=mid)t[u].ls=ins(l,mid,t[k].ls,p);
    else t[u].rs=ins(mid+1,r,t[k].rs,p);
    return u;
}
int query(int l,int r,int pre,int now,int k){
    if(t[now].ls==t[now].rs){
        return b[l];
    }
    int mid=(l+r)/2,tmp=t[t[now].ls].v-t[t[pre].ls].v;
    if(tmp>=k)return query(l,mid,t[pre].ls,t[now].ls,k);
    else return query(mid+1,r,t[pre].rs,t[now].rs,k-tmp);
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    rt[0]=build(1,n);
    //printf("a\n");
    for(int i=1;i<=n;i++)rt[i]=ins(1,n,rt[i-1],lower_bound(b+1,b+n+1,a[i])-b);
    for(int i=1;i<=q;i++){
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(1,n,rt[l-1],rt[r],k));
    }
    return 0;
}

帶修改區間k大(主席樹套樹狀數組)


發佈了36 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章