主席树的各类模板(区间第k大数动,静,区间不同数的个数,区间<=k的个数)
Posted shuaihui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主席树的各类模板(区间第k大数动,静,区间不同数的个数,区间<=k的个数)相关的知识,希望对你有一定的参考价值。
1.(HDOJ2665)http://acm.hdu.edu.cn/showproblem.php?pid=2665
(POJ2104)http://poj.org/problem?id=2104
(POJ2761)http://poj.org/problem?id=2761
题意:求区间第K大,主席树模板题
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=200010; int tot,n,q,nowm; int a[maxn],t[maxn]; int c[maxn<<5],lson[maxn<<5],rson[maxn<<5]; int T[maxn]; void init_hash() { for ( int i=1;i<=n;i++ ) t[i]=a[i]; sort(t+1,t+1+n); nowm=unique(t+1,t+1+n)-(t+1); } int hash_(int x) { return lower_bound(t+1,t+1+nowm,x)-t; } void build(int &root,int l,int r) { root=++tot; if ( l==r ) return; int mid=(l+r)/2; build(lson[root],l,mid); build(rson[root],mid+1,r); } void update(int root,int &rt,int p,int val,int l,int r) { rt=++tot; lson[rt]=lson[root],rson[rt]=rson[root]; c[rt]=c[root]+val; if ( l==r ) return; int mid=(l+r)/2; if ( p<=mid ) update(lson[rt],lson[rt],p,val,l,mid); else update(rson[rt],rson[rt],p,val,mid+1,r); } int query(int rt_,int rt,int l,int r,int k) { if ( l==r ) return l; int mid=(l+r)/2; int sum=c[lson[rt_]]-c[lson[rt]]; if ( sum>=k ) return query(lson[rt_],lson[rt],l,mid,k); else return query(rson[rt_],rson[rt],mid+1,r,k-sum); } int main() { int Case; scanf("%d",&Case); while(Case++){ scanf("%d%d",&n,&q); tot=0; for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]); init_hash(); build(T[0],1,nowm); for ( int i=1;i<=n;i++ ) { int pos=hash_(a[i]); update(T[i-1],T[i],pos,1,1,nowm); } while ( q-- ) { int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\\n",t[query(T[r],T[l-1],1,nowm,k)]); } } return 0; }
2.(HDOJ4417)http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:求给定区间<=k的数有多少
分析:在模板上将query部分修改一下即可,对于区间[L,R]来说,只需要将第R颗线段树上的[0,k]区间内的值减去第L-1颗线段树上对应区间即可。离线在线都行,离线做法需要将每次访问的k也添加进入hash数组,而对于在线来说转化后的数转化前相对于给定的k来说只能变小不能变大即可
注意:题目给的区间范围从0开始,要将其转化成从1开始
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
const int maxm=3e6+10;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];
void init_hash()
{
for ( int i=1;i<=n;i++ ) t[i]=a[i];
sort(t+1,t+1+n);
m=unique(t+1,t+1+n)-(t+1);
}
int build(int l,int r)
{
int root=tot++;
c[root]=0;
if ( l!=r )
{
int mid=(l+r)/2;
lson[root]=build(l,mid);
rson[root]=build(mid+1,r);
}
return root;
}
int hash_(int x)
{
return lower_bound(t+1,t+1+m,x)-t;
}
int update(int root,int pos,int val)
{
int rt=tot++,tmp=rt;
c[rt]=c[root]+val;
int l=1,r=m;
while ( l<r )
{
int mid=(l+r)/2;
if ( pos<=mid )
{
lson[rt]=tot++;rson[rt]=rson[root];
rt=lson[rt];root=lson[root];
r=mid;
}
else
{
rson[rt]=tot++;lson[rt]=lson[root];
rt=rson[rt];root=rson[root];
l=mid+1;
}
c[rt]=c[root]+val;
}
return tmp;
}
int query(int lrt,int rrt,int k)
{
int ret=0;
int l=1,r=m;
while ( l<r )
{
int mid=(l+r)/2;
if ( k<=mid )
{
r=mid;
lrt=lson[lrt];
rrt=lson[rrt];
}
else
{
ret+=c[lson[rrt]]-c[lson[lrt]];
l=mid+1;
lrt=rson[lrt];
rrt=rson[rrt];
}
}
ret+=c[rrt]-c[lrt];
return ret;
}
int main()
{
int Case,h;
scanf("%d",&Case);
for ( h=1;h<=Case;h++ )
{
scanf("%d%d",&n,&q);
tot=0;
for ( int i=1;i<=n;i++ )
{
scanf("%d",&a[i]);
a[i]++;
}
init_hash();
T[0]=build(1,m);
for ( int i=1;i<=n;i++ )
{
int pos=hash_(a[i]);
T[i]=update(T[i-1],pos,1);
}
printf("Case %d:\\n",h);
while ( q-- )
{
int l,r,k,p;
scanf("%d%d%d",&l,&r,&k);
l++,r++,k++;
p=hash_(k);
if ( t[p]>k ) p--;
if ( p==0 ) printf("0\\n");
else printf("%d\\n",query(T[l-1],T[r],p));
}
}
return 0;
}
HDOJ4417(在线)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1e5+10;
const int maxm=3e6+10;
int n,q,m,tot;
int a[maxn],t[maxn*2],l[maxn],r[maxn],val[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];
void init_hash()
{
for ( int i=1;i<=n;i++ ) t[i]=a[i];
for ( int i=1;i<=q;i++ ) t[i+n]=val[i];
sort(t+1,t+1+n+q);
m=unique(t+1,t+1+n+q)-(t+1);
}
int build(int l,int r)
{
int root=tot++;
c[root]=0;
if ( l!=r )
{
int mid=(l+r)/2;
lson[root]=build(l,mid);
rson[root]=build(mid+1,r);
}
return root;
}
int hash_(int x)
{
return lower_bound(t+1,t+1+m,x)-t;
}
int update(int root,int pos,int val)
{
int rt=tot++,tmp=rt;
c[rt]=c[root]+val;
int l=1,r=m;
while ( l<r )
{
int mid=(l+r)/2;
if ( pos<=mid )
{
lson[rt]=tot++;rson[rt]=rson[root];
rt=lson[rt];root=lson[root];
r=mid;
}
else
{
rson[rt]=tot++;lson[rt]=lson[root];
rt=rson[rt];root=rson[root];
l=mid+1;
}
c[rt]=c[root]+val;
}
return tmp;
}
int query(int lrt,int rrt,int k)
{
int ret=0;
int l=1,r=m;
while ( l<r )
{
int mid=(l+r)/2;
if ( k<=mid )
{
r=mid;
lrt=lson[lrt];
rrt=lson[rrt];
}
else
{
ret+=c[lson[rrt]]-c[lson[lrt]];
l=mid+1;
lrt=rson[lrt];
rrt=rson[rrt];
}
}
ret+=c[rrt]-c[lrt];
return ret;
}
int main()
{
int Case,h;
scanf("%d",&Case);
for ( h=1;h<=Case;h++ )
{
scanf("%d%d",&n,&q);
tot=0;
for ( int i=1;i<=n;i++ )
{
scanf("%d",&a[i]);
a[i]++;
}
for ( int i=1;i<=q;i++ ) {
scanf("%d%d%d",&l[i],&r[i],&val[i]);
l[i]++,r[i]++,val[i]++;
}
init_hash();
T[0]=build(1,m);
for ( int i=1;i<=n;i++ )
{
int pos=hash_(a[i]);
T[i]=update(T[i-1],pos,1);
}
printf("Case %d:\\n",h);
for ( int i=1;i<=q;i++ )
{
int L,R,k;
L=l[i],R=r[i],k=val[i];
k=hash_(k);
printf("%d\\n",query(T[L-1],T[R],k));
}
}
return 0;
}
HDOJ4417(离线)
3.(SPOJ3267)http://www.spoj.com/problems/DQUERY/
题意:给出一个长度为n 的数列,有q 个询问,每个询问给出数对 [i,j],需要你给出这一段中有多少不同的数字
分析:利用map记录每个数的位置,主席树建新树的时候,如果当前元素出现过,那么把这个元素上次出现的位置减一,然后当前位置加一,如果没出现过就是普通的建树操作。
对于查询[l, r]我们只需要取出第r棵树,然后输出这棵树[l,r]之间的和,因为是按从1到n的顺序插入的,所以每次只需要求>=l的个数即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int maxn=3e4+10;
const int maxm=3e6+10;
int n,q,tot;
int a[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];
int build(int l,int r)
{
int root=tot++;
c[root]=0;
if ( l!=r )
{
int mid=(l+r)/2;
lson[root]=build(l,mid);
rson[root]=build(mid+1,r);
}
return root;
}
int update(int root,int pos,int val)
{
int rt=tot++,tmp=rt;
c[rt]=c[root]+val;
int l=1,r=n;
while ( l<r )
{
int mid=(l+r)/2;
if ( pos<=mid )
{
lson[rt]=tot++;rson[rt]=rson[root];
rt=lson[rt];root=lson[root];
r=mid;
}
else
{
rson[rt]=tot++;lson[rt]=lson[root];
rt=rson[rt];root=rson[root];
l=mid+1;
}
c[rt]=c[root]+val;
}
return tmp;
}
int query(int rt,int lpos)
{
int ret=0;
int l=1,r=n;
while ( lpos>l )
{
int mid=(l+r)/2;
if ( lpos<=mid )
{
r=mid;
ret+=c[rson[rt]];
rt=lson[rt];
}
else
{
rt=rson[rt];
l=mid+1;
}
}
return ret+c[rt];
}
int main()
{
int Case;
while ( scanf("%d",&n)!=EOF )
{
tot=0;
for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]);
T[0]=build(1,n);
map<int,int>mp;
for ( int i=1;i<=n;i++ )
{
if ( mp.find(a[i])!=mp.end() )
{
int tmp=update(T[i-1],mp[a[i]],-1);
T[i]=update(tmp,i,1);
}
else T[i]=update(T[i-1],i,1);
mp[a[i]]=i;
}
scanf("%d",&q);
while ( q-- )
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\\n",query(T[r],l));
}
}
return 0;
}
SPOJ3267
4.(ZOJ2112)http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112
题意:给定一串序列,有两种操作,一种是求区间[l,r]第k大,另外一种是将a[i]=t
带修改的主席树
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=60010;
const int maxm=2500010;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[maxn],lson[maxm],rson[maxm],c[maxm];
int S[maxn];
struct Query{
int kind;
int l,r,k;
}query[10010];
void init_hash(int k)
{
sort(t+1,t+k+1);
m=unique(t+1,t+k+1)-(t+1);
}
int hash_(int x)
{
return lower_bound(t+1,t+m+1,x)-t;
}
int build(int l,int r)
{
int root=tot++;
c[root]=0;
if ( l!=r )
{
int mid=(l+r)/2;
lson[root]=build(l,mid);
rson[root]=build(mid+1,r);
}
return root;
}
int update(int root,int pos,int val)
{
int rt=tot++,tmp=rt;
c[rt]=c[root]+val;
int l=1,r=m;
while ( l<r )
{
int mid=(l+r)/2;
if ( pos<=mid )
{
lson[rt]=tot++;rson[rt]=rson[root];
rt=lson[rt];root=lson[root];
r=mid;
}
else
{
rson[rt]=tot++;lson[rt]=lson[root];
rt=rson[rt];root=rson[root];
l=mid+1;
}
c[rt]=c[root]+val;
}
return tmp;
}
int lowbit(int x)
{
return x&(-x);
}
int used[maxn];
void add(int x,int pos,int val)
{
while ( x<=n )
{
S[x]=update(S[x],pos,val);
x+=lowbit(x);
}
}
int sum(int x)
{
int ret=0;
while ( x>0 )
{
ret+=c[lson[used[x]]];
x-=lowbit(x);
}
return ret;
}
int Q(int left,int right,int k)
{
int lrt=T[left];
int rrt=T[right];
int l=1,r=m;
for ( int i=left;i>0;i-=lowbit(i)) used[i]=S[i];
for ( int i=right;i>0;i-=lowbit(i)) used[i]=S[i];
while ( l<r )
{
int mid=(l+r)/2;
int tmp=sum(right)-sum(left)+c[lson[rrt]]-c[lson[lrt]];
if ( tmp>=k )
{
r=mid;
for ( int i=left;i>0;i-=lowbit(i)) used[i]=lson[used[i]];
for ( int i=right;i>0;i-=lowbit(i)) used[i]=lson[used[i]];
lrt=lson[lrt];
rrt=lson[rrt];
}
else
{
l=mid+1;
k-=tmp;
for ( int i=left;i>0;i-=lowbit(i)) used[i]=rson[used[i]];
for ( int i=right;i>0;i-=lowbit(i)) used[i]=rson[used[i]];
lrt=rson[lrt];
rrt=rson[rrt];
}
}
return l;
}
int main()
{
int Case;
scanf("%d",&Case);
while ( Case-- )
{
scanf("%d%d",&n,&q);
tot=0;
m=0;
for ( int i=1;i<=n;i++ )
{
scanf("%d",&a[i]);
t[++m]=a[i];
}
char op[10];
for ( int i=0;i<q;i++ )
{
scanf("%s",op);
if ( op[0]==\'Q\' )
{
query[i].kind=0;
scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
}
else
{
query[i].kind=1;
scanf("%d%d",&query[i].l,&query[i].r);
t[++m]=query[i].r;
}
}
init_hash(m);
T[0]=build(1,m);
for ( int i=1;i<=n;i++ )
{
int pos=hash_(a[i]);
T[i]=update(T[i-1],pos,1);
}
for ( int i=1;i<=n;i++ ) S[i]=T[0以上是关于主席树的各类模板(区间第k大数动,静,区间不同数的个数,区间<=k的个数)的主要内容,如果未能解决你的问题,请参考以下文章
hdu 5919 主席树(区间不同数的个数 + 区间第k大)