zoukankan      html  css  js  c++  java
  • 主席树入门

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

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

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

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

    我想修改的话就必须搞一波树套树了(尽管我不会但是 我还是不会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

    效果当然还不错.

  • 相关阅读:
    基于UML的科研管理系统 的设计与实现06
    基于UML的科研管理系统 的设计与实现05
    基于UML的科研管理系统 的设计与实现04
    基于UML的科研管理系统 的设计与实现03
    《暗时间》读后感
    毕业设计之文献综述目录
    基于UML的科研管理系统 的设计与实现02
    基于UML的科研项目管理系统的设计与实现01
    【手撕】中序遍历二叉树
    【手撕】二叉树前序遍历
  • 原文地址:https://www.cnblogs.com/chdy/p/10422492.html
Copyright © 2011-2022 走看看