主席树入门

  刷了那么多线段树的题终于可以搞一下可持久化线段树了。

首先是 放一道最最最简单的例题:

对于这个需要访问历史的线段树来说 我想应该是一棵可持久化线段树的东西。

对于一棵 线段树我们想让其可持久化 那么应该是 让其点接着以前的点接(如果没有修改的话)

我想修改的话就必须搞一波树套树了(尽管我不会但是 我还是不会HH)

其实把线段树学通了,那么这个 主席树当然也算是很简单的了。

 //#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define h(p) t[p].h
#define l(p) t[p].x
#define k(p) t[p].k
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
const int MAXN=1000002;
struct wy
{
    int lc,rc;
    int v;
}t[MAXN<<4];
int tot,root[MAXN];
int n,m,cnt;
int a[MAXN],ans;
int build(int l,int r)
{
    int p=++tot;
    if(l==r){t[p].v=a[l];return p;}
    int mid=(l+r)>>1;
    t[p].lc=build(l,mid);
    t[p].rc=build(mid+1,r);
    return p;
}
int insert(int now,int l,int r,int x,int d)
{
    int p=++tot;
    t[p]=t[now];
    if(l==r)
    {
        t[p].v=d;
        return p;
    }
    int mid=(l+r)>>1;
    if(x<=mid)t[p].lc=insert(t[p].lc,l,mid,x,d);
    else t[p].rc=insert(t[p].rc,mid+1,r,x,d);
    return p;
}
void ask(int p,int l,int r,int x)
{
    if(l==r){ans=t[p].v;return;}
    int mid=(l+r)>>1;
    if(x<=mid)ask(t[p].lc,l,mid,x);
    else ask(t[p].rc,mid+1,r,x);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    root[0]=build(1,n);
    for(int i=1;i<=m;i++)
    {
        int p,mark,x,d;
        p=read();mark=read();
        if(mark==1)
        {
            x=read();d=read();
            root[++cnt]=insert(root[p],1,n,x,d);
        }
        if(mark==2)
        {
            x=read();
            root[++cnt]=++tot;
            t[tot]=t[root[p]];
            ask(root[p],1,n,x);
            put(ans);
        }
    }
    //cout<<endl;
    //for(int i=0;i<=cnt;i++)put(root[i]);
    return 0;
}
View Code

这道题 呢 很显然 (题目告诉我们是一棵主席树且不带区间修改求第k小)
那么我们 呢 可以搞一波事情 。

就是 线段树直接的相加减可以得到区间的信息 ,当然线段树要建一棵权值线段树。

千万不要像我一样直接码 因为码到最后一行输出意识到自己没用线段树相加减 (自己写的当时也没有这个想法)

然后就是感觉对的,然后到最后一行意识到自己建的主席树完全错了。

于是全删了愚蠢的思路我就不再提了这里我们要建 nlogn棵线段树即可解决问题!

 //#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 2147483646
#define h(p) t[p].h
#define l(p) t[p].x
#define k(p) t[p].k
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
const int MAXN=100002,maxn=5001;
struct wy
{
    int lc,rc;
    int v;
}t[MAXN<<5];//其实准确(较粗略)的空间复杂度为 4*n+nlogn==(4+logn)*n logn=6/0,3=20 24*n
int n,m,q;
int a[MAXN],b[MAXN];
int root[MAXN],cnt,tot,ans;
void discrete()
{
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++)if(i==1||b[i]!=b[i-1])b[++m]=b[i];
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b;
}
int build(int l,int r)
{
    int p=++tot;
    if(l==r)return p;
    int mid=(l+r)>>1;
    build(l,mid);
    build(mid+1,r);
    return p;
}
void ask(int x,int y,int l,int r,int k)
{
    if(l==r){ans=l;return;}
    int c=t[t[y].lc].v-t[t[x].lc].v;
    int mid=(l+r)>>1;
    if(k>c)ask(t[x].rc,t[y].rc,mid+1,r,k-c);
    else ask(t[x].lc,t[y].lc,l,mid,k);
    return;
}
int insert(int now,int l,int r,int d)
{
    int p=++tot;
    t[p]=t[now];
    if(l==r){t[p].v=t[now].v+1;return p;}
    int mid=(l+r)>>1;
    if(d<=mid)t[p].lc=insert(t[p].lc,l,mid,d);
    else t[p].rc=insert(t[p].rc,mid+1,r,d);
    t[p].v=t[t[p].lc].v+t[t[p].rc].v;
    return p;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();q=read();
    for(int i=1;i<=n;i++)a[i]=b[i]=read();
    discrete();
    //for(int i=1;i<=n;i++)cout<<a[i]<<' '<<b[a[i]]<<endl;
    root[0]=build(1,n);
    for(int i=1;i<=n;i++)root[i]=insert(root[i-1],1,n,a[i]);
    for(int i=1;i<=q;i++)
    {
        int x,y,k;
        x=read();y=read();k=read();
        ask(root[x-1],root[y],1,n,k);
        put(b[ans]);
    }
    return 0;
}
View Code

那么这道题呢 比较难想吧因为 要维护的东西很多看 n,m的范围然后 很迷啊,怎么求呢。

首先考虑两点之间的点权 怎么搞 肯定是LCA 的题!

但是没有那么简单 求两点 之间的点权第k小的 那么 尧神是小小的点播了一番我

先做这样的一个题目 求 任意两点之间的点权之和 

被嘲讽了,然后想出 对于一个节点每个维护一到根节点之间的点权之和即可在求出LCA的情况之下 
O(1)的时间内求出一些东西!

有两种解决方法 d[x]+d[y]-2*d[LCA]+v[LCA]     d[x]+d[y]-d[LCA]-d[fLCA] 

都是可以的 然后就是 一波主席树 没了

值得一提的是第一种方法有坑点 我犯了一个低级错误23333

于是有了如下代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    return;
}
const int MAXN=100002;
int n,m,T,s=1,q[MAXN],w;
int a[MAXN],b[MAXN];
int depth[MAXN],f[MAXN][25];//f[i][j]表示i节点向上爬2^j个单位的祖先
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],len=0;
int root[MAXN],cnt,tot,ans;
struct wy
{
    int lc,rc;
    int v;
}t[MAXN<<5];
void discrete()
{
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++)if(i==1||b[i]!=b[i-1])b[++m]=b[i];
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b;
}
void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
void bfs()
{
    int h=0,t=0;
    q[++t]=s;
    depth[s]=1;
    while(h++<t)
    {
        int te=q[h];
        for(int i=lin[te];i;i=nex[i])
        {
            int tn=ver[i];
            if(depth[tn]!=0)continue;
            depth[tn]=depth[te]+1;
            f[tn][0]=te;
            for(int j=1;j<=w;j++)f[tn][j]=f[f[tn][j-1]][j-1];
            q[++t]=tn;
        }
    }
}
int LCA(int x,int y)
{
    if(depth[x]>depth[y]){int t;t=x;x=y;y=t;}
    if(depth[x]!=depth[y])
        for(int i=w;i>=0;i--)
            if(depth[f[y][i]]>=depth[x])y=f[y][i];
    if(x==y)return x;
    for(int i=w;i>=0;i--)
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int build(int l,int r)
{
    int p=++tot;
    if(l==r)return p;
    int mid=(l+r)>>1;
    t[p].lc=build(l,mid);
    t[p].rc=build(mid+1,r);
    return p;
}
int insert(int now,int l,int r,int d)
{
    int p=++tot;
    t[p]=t[now];
    if(l==r){t[p].v=t[now].v+1;return p;}
    int mid=(l+r)>>1;
    if(d<=mid)t[p].lc=insert(t[p].lc,l,mid,d);
    else t[p].rc=insert(t[p].rc,mid+1,r,d);
    t[p].v=t[t[p].lc].v+t[t[p].rc].v;
    return p;
}
void dfs(int x,int father)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        root[tn]=insert(root[x],1,m,a[tn]);
        dfs(tn,x);
    }
}
void ask(int lca,int x,int y,int l,int r,int d,int k)//公式显然为 t[x]+t[y]-2*t[lca]+(l<=d<=mid)?1:0;
{
    if(l==r){ans=l;return;}
    int c=0;
    int mid=(l+r)>>1;
    if(d<=mid&&d>=l)c++;//注意!!!
    c+=t[t[x].lc].v+t[t[y].lc].v-2*t[t[lca].lc].v;
    if(c<k)ask(t[lca].rc,t[x].rc,t[y].rc,mid+1,r,d,k-c);
    else ask(t[lca].lc,t[x].lc,t[y].lc,l,mid,d,k);
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();T=read();
    for(int i=1;i<=n;i++)a[i]=b[i]=read();
    discrete();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
    }
    w=(int)(log(n*1.0)/log(2*1.0))+1;
    bfs();
    root[cnt]=build(1,m);
    root[s]=insert(root[cnt],1,m,a[s]);
    dfs(s,0);
    //cout<<m<<endl;
    //cout<<b[m]<<endl;
    //for(int i=1;i<=n;i++)put(t[root[i]].v);
    //cout<<T<<endl;
    for(int i=1;i<=T;i++)
    {
        int x,y,k;
        x=read();y=read();k=read();
        x=x^ans;int lca=LCA(x,y);
        //cout<<x<<' '<<y<<endl;
        //cout<<(x^ans)<<' '<<lca<<endl;
        ask(root[lca],root[x],root[y],1,m,a[lca],k);
        //cout<<ans<<endl;
        ans=b[ans];
        put(ans);
        if(i!=T)puts("");
    }
    return 0;
}
View Code

效果当然还不错.

原文地址:https://www.cnblogs.com/chdy/p/10422492.html