不带入门!(以后有空可能会写?)
先来个入门福利题
[福利]可持久化线段树
题目描述
为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。
线段树有个非常经典的应用是处理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
他们路径上的点构成的权值线段树就是
细节较多。
本人代码教丑….
#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;
}
带修改的主席树。