主席树(未完成)

不带入门!(以后有空可能会写?)

先来个入门福利题

[福利]可持久化线段树

题目描述
为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。

线段树有个非常经典的应用是处理RMQ问题,即区间最大/最小值询问问题。现在我们把这个问题可持久化一下:

Q k l r 查询数列在第k个版本时,区间[l, r]上的最大值

M k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本

最开始会给你一个数列,作为第1个版本。

每次M操作会导致产生一个新的版本。修改操作可能会很多呢,如果每次都记录一个新的数列,空间和时间上都是令人无法承受的。所以我们需要可持久化数据结构:

对于最开始的版本1,我们直接建立一颗线段树,维护区间最大值。

修改操作呢?我们发现,修改只会涉及从线段树树根到目标点上一条树链上logn个节点而已,其余的节点并不会受到影响。所以对于每次修改操作,我们可以只重建修改涉及的节点即可。就像这样:

需要查询第k个版本的最大值,那就从第k个版本的树根开始,像查询普通的线段树一样查询即可。 要计算好所需空间哦

输入格式
第一行两个整数N, Q。N是数列的长度,Q表示询问数

第二行N个整数,是这个数列

之后Q行,每行以0或者1开头,0表示查询操作Q,1表示修改操作M,格式为

0 k l r 查询数列在第k个版本时,区间[l, r]上的最大值 或者

1 k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本

输出格式
对于每个询问,输出正确答案

样例数据
input

4 5
1 2 3 4
0 1 1 4
1 1 3 5
0 2 1 3
0 2 4 4
0 1 2 4
output

4
5
4
4
样例解释

序列版本1: 1 2 3 4
查询版本1的[1, 4]最大值为4
修改产生版本2: 1 2 5 4
查询版本2的[1, 3]最大值为5
查询版本1的[4, 4]最大值为4
查询版本1的[2, 4]最大值为4
数据规模与约定
N <= 10000 Q <= 100000 对于每次询问操作的版本号k保证合法, 区间[l, r]一定满足1 <= l <= r <= N

出题人: sxysxy。原题见: http://syzoj.com/problem/247

时间限制:3s3s
空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
int n,m,t,t1,f[101000],a[10100],ans; 
struct tree
{
    int ls,rs,max;
}tree[101000*21];
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
}
void build_new(int &root,int l,int r) 
{
    root=++t; 
    if(l==r) return; 
    int mid=(l+r)>>1;
    build_new(tree[root].ls,l,mid); 
    build_new(tree[root].rs,mid+1,r); 
}
void build1(int root,int l,int r,int num,int i)
{
    if(num>tree[root].max) tree[root].max=num; 
    if(l==r) return;
    int mid=(l+r)>>1;
    if(i<=mid) build1(tree[root].ls,l,mid,num,i);
    else build1(tree[root].rs,mid+1,r,num,i);
    return;
}
void init()
{
    t=0; 
    n=read();m=read();
    build_new(f[0],1,n);
    for(int i=1;i<=n;++i) a[i]=read(),build1(1,1,n,a[i],i);
    f[++t1]=1; 
    return;
}
void build(int &root,int last,int l,int r,int p,int num)
{
    root=++t;
    if(l==r) {tree[root].max=num;return;}
    tree[root].ls=tree[last].ls;
    tree[root].rs=tree[last].rs;
    int mid=(l+r)>>1; 
    if(p<=mid) build(tree[root].ls,tree[last].ls,l,mid,p,num);
    else build(tree[root].rs,tree[last].rs,mid+1,r,p,num);  
    tree[root].max=max(tree[tree[root].ls].max,tree[tree[root].rs].max);
    return;
}
void find(int root,int ll,int rr,int l,int r)
{     
    if(ll>r||rr<l) return;
    if(ll<=l&&rr>=r) {ans=max(ans,tree[root].max);return;}
    if(l==r) return;
    int mid=(l+r)>>1;
    find(tree[root].ls,ll,rr,l,mid);
    find(tree[root].rs,ll,rr,mid+1,r);
    return;
}
void work()
{
    for(int i=1;i<=m;++i)
     {
      int xx=read();
      if(xx==0) 
       {
        int k=read(),l=read(),r=read();
        ans=0;
        find(f[k],l,r,1,n);
        printf("%d\n",ans);
       }
       else 
       {
        int k=read(),p=read(),num=read();
        build(f[++t1],f[k],1,n,p,num);  
       }
     }
    return;
}
int main()
{
    init();
    work();
    return 0; 
}

正式主席树模板题

[主席树]区间第k小

题目描述
很久很久以前,在维斯特洛大陆,存在着一个庞大的家族,名为高级数据结构。这个家族的姓为“树”,这个家族有着辉煌的历史,早在英雄纪元,这个家族就存在着很多伟大的人物,比如:线段?树,平衡?树,替罪羊?树,主席?树等等等等。

显然,这个家族的组长为主席?树。高级数据结构家族的每个成员都有三个属性,分别为时间复杂度,空间复杂度和代码复杂度。这三个属性的乘积代表了每个成员的权值。

某日,主席?树想吹逼,于是他让所有成员站成了一条序列。他竟然可以很快的回答出来再这个序列上区间[L,R]的第K大的成员的权值!看到了旁边树套树套树套树学妹投来了羡慕的眼光,他绝对把这个方法教给她,但是他现需要你帮他写一个模板!

一句话题意,给定一个长为N的序列,每个序列的权值为Ai.有M个询问,每个询问为(L,R,K),分别代表[L,R]的第K小的数。

输入格式
第一行有两个数N,M。 接下来有N个数,每个数用空格分开,第i个数为Ai。 接下来有M行,每行一个询问,分别为L,R,K。

输出格式
对于每一行,输出一个询问对应的答案。

input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
output

5
6
3
数据规模与约定
N=100000 M=5000

Ai<=1000000

时间限制:1s

空间限制:256MB

经典题

水水的data by Cydiater

过了的可以在:http://poj.org/problem?id=2104 这里再提交一遍

#include<bits/stdc++.h>
using namespace std;
int n,m,hash[101000],t,t1,hash_new[101000],f[101000]; //hash表示数字与等级的映射,hash_new代表新数组 
struct tree
{
    int ls,rs,num;
}tree[101000*22];
struct old
{
    int num,id;
}a[101000];
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
}
bool mycup(old a,old b){return a.num<b.num;}
void init()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
     {
      a[i].num=read();
      a[i].id=i;
     } 
    sort(a+1,a+n+1,mycup);
    for(int i=1;i<=n;++i)
     if(a[i].num!=a[i-1].num)
      hash[++t]=a[i].num,hash_new[a[i].id]=t;
     else
      hash_new[a[i].id]=t;
    return;
 }
void build(int &root,int last,int l,int r,int num)
{
    root=++t1;
    tree[root].num=tree[last].num+1;
    int mid=(l+r)>>1;
    tree[root].ls=tree[last].ls;
    tree[root].rs=tree[last].rs;
    if(l==r)  return;
    if(num<=mid) build(tree[root].ls,tree[last].ls,l,mid,num);
    else build(tree[root].rs,tree[last].rs,mid+1,r,num);
    return;
}
int find(int root,int last,int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int sum=tree[tree[last].ls].num-tree[tree[root].ls].num;
    if(k<=sum) return find(tree[root].ls,tree[last].ls,l,mid,k);
    else return find(tree[root].rs,tree[last].rs,mid+1,r,k-sum);
}
void work()
{
    for(int i=1;i<=n;++i)
     build(f[i],f[i-1],1,t,hash_new[i]);
    for(int i=1;i<=m;++i)
     {
      int l=read(),r=read(),k=read();
      printf("%d\n",hash[find(f[l-1],f[r],1,t,k)]);
     }
    return;
}
int main()
{
    init();
    work();
    return 0; 
}

进阶题:

bzoj2588 Spoj 10628. Count on a tree

题目描述
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。

其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

输入格式
第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v,k),表示一组询问。

输出格式
M行,表示每个询问的答案。最后一个询问不输出换行符

样例数据
input

8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2
output

2
8
9
105
7
数据规模与约定
HINT:N,M<=100000 暴力自重。。。

时间限制:1s1s
空间限制:256MB

树上处理点之间路径问题的,我们想到dfs序。
我们按照dfs序建立权值线段树 建成主席树。每次出点都在主席树立删去。

那么对于点对u,v
他们路径上的点构成的权值线段树就是
t r e e [ u ] + t r e e [ v ] t r e e [ l a c ( u , v ) ] t r e e [ f a t h e r [ l c a ( u , v ) ] ]
细节较多。
本人代码教丑….

#include<bits/stdc++.h>
using namespace std;
int n,m,hash[101000],t,t1,tp,hash_new[101000],bj[101000],f[101000],deep[101000],lastans; //hash表示数字与等级的映射,hash_new代表新数组 
int fa[101000][21],f_a,f_b,f_x,f_y;
struct node{int n,y;}e[201000];
int linkk[101000],te,dfs_in[101000],dfs_out[101000],tt;
bool vis[101000];
struct tree{int ls,rs,num;}tree[101000*50];
struct old{int num,id;}a[101000];
struct point{int num,time;}a_p[101000];
struct dfsa{int num,place;}dfs_end[101000];
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
}
bool mycup(old a,old b){return a.num<b.num;}
bool mycup1(dfsa a,dfsa b){return a.place<b.place;}
bool mycup2(point a,point b){return a.time<b.time;}
void dfs(int x,int father)
{
    vis[x]=true;
    a_p[x].time=++tp;
    deep[x]=deep[father]+1;  
    dfs_in[x]=++tt;
    fa[x][0]=father;
    for(int i=linkk[x];i;i=e[i].n)
     {  
      if(!vis[e[i].y]) dfs(e[i].y,x);
     }    
    dfs_out[x]=tt;
}
void init()
{
    memset(fa,-1,sizeof(fa));
    n=read();m=read();
    for(int i=1;i<=n;++i)
     {
      a[i].num=read();
      a[i].id=i;
      a_p[i].num=i; 
     } 
    sort(a+1,a+n+1,mycup);
    for(int i=1;i<=n;++i)
     if(a[i].num!=a[i-1].num)
      hash[++t]=a[i].num,hash_new[a[i].id]=t;
     else
      hash_new[a[i].id]=t;
    for(int i=1;i<=n-1;++i) 
     {
      int aa=read(),bb=read();
      e[++te].y=bb;
      e[te].n=linkk[aa]; 
      linkk[aa]=te; 
      e[++te].y=aa;
      e[te].n=linkk[bb];
      linkk[bb]=te;
     }
    dfs(1,0);
    for(int i=1;i<=n;++i) dfs_end[i].num=i,dfs_end[i].place=dfs_out[i]+1; 
    sort(dfs_end+1,dfs_end+n+1,mycup1);
    return;
}
void delite(int &root,int last,int l,int r,int num)
{
    if(!(l==1&&r==n)) 
     {
      root=++t1;
      tree[root].ls=tree[last].ls;
      tree[root].rs=tree[last].rs;
     }
    tree[root].num=tree[last].num-1;
    int mid=(l+r)>>1;
    if(l==r) return;
    if(num<=mid) delite(tree[root].ls,tree[last].ls,l,mid,num);
    else delite(tree[root].rs,tree[last].rs,mid+1,r,num);
}
void build(int &root,int last,int l,int r,int num)
{
    root=++t1;
    tree[root].num=tree[last].num+1;
    int mid=(l+r)>>1;
    tree[root].ls=tree[last].ls;
    tree[root].rs=tree[last].rs;
    if(l==r)  return;
    if(num<=mid) build(tree[root].ls,tree[last].ls,l,mid,num);
    else build(tree[root].rs,tree[last].rs,mid+1,r,num);
    return;
}
void change(bool flag)
{
    if(flag)
     {
      f_a=tree[f_a].ls;
      f_b=tree[f_b].ls;
      f_x=tree[f_x].ls;
      f_y=tree[f_y].ls;
     }
    else
     {
      f_a=tree[f_a].rs;
      f_b=tree[f_b].rs;
      f_x=tree[f_x].rs;
      f_y=tree[f_y].rs;
     }
    return;
}
int find(int l,int r,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int sum=tree[tree[f_x].ls].num+tree[tree[f_y].ls].num-tree[tree[f_a].ls].num-tree[tree[f_b].ls].num;
    if(k<=sum) return change(1),find(l,mid,k);
    else return change(0),find(mid+1,r,k-sum);
}
int lca(int a,int b)
{
    if(deep[a]<deep[b]) swap(a,b);
    int i;
    for(i=0;(1<<i)<=deep[a];i++); i--;
    for(int j=i;j>=0;j--) 
     if(deep[a]-(1<<j)>=deep[b]) a=fa[a][j]; 
    if(a==b) return a;
    for(int j=i;j>=0;j--)
     if(fa[a][j]!=0&&fa[a][j]!=fa[b][j])
      a=fa[a][j],b=fa[b][j];
    return fa[a][0];
}
void work()
{ 
    memset(tree,0,sizeof(tree));
    int j=1;
    sort(a_p+1,a_p+n+1,mycup2);
    for(int i=1;i<=n;++i) 
     {
      build(f[i],f[i-1],1,n,hash_new[a_p[i].num]);
      bj[a_p[i].num]=i;
      while(dfs_end[j].place==i) delite(f[i],f[i],1,n,hash_new[dfs_end[j].num]),j++; 
     } 
    for(int i=1;i<=20;++i)
      for(int j=1;j<=n;++j)
        fa[j][i]=fa[fa[j][i-1]][i-1];
    for(int i=1;i<=m;++i)
     {
      f_x=read(),f_y=read();
      f_x^=lastans;
      int w=lca(f_x,f_y);
      f_x=f[bj[f_x]];f_y=f[bj[f_y]];  
      f_a=f[bj[w]];f_b=f[bj[fa[w][0]]];
      int zz=read();     
      lastans=hash[find(1,n,zz)]; 
      printf("%d\n",lastans);
     }
    return;
}
int main()
{
//  freopen("data1.in","r",stdin);
//  freopen("data1.out","w",stdout);
    init();
    work();
    return 0; 
}

带修改的主席树。

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/79475545