主席树+离散
1.利用前缀和性质,建立n棵权值线段树(1-i区间内有几个数离散化后的值在树上每个结点对应区间内)
2.查询i到j区间只需将第j棵树减去第i-1棵树,得到表示i到j区间内情况的树,线段树查询即可。
建树O(nlogn),查询每次O(logn)
注意:待修主席树为了修改方便(和建树共用一个函数),开点时常用if(!rt) rt=con-1;这样修改的时候不会新开节点。
但是主席树建树每个rt必须开新的结点,不能有 if(!rt) ,因为tree[con++]=tree[rt],如果不开新节点,之前的结点会被修改。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,con=1,rt[100010],num[100010],p[100010];
struct node
{
int l,r,sum;
}tree[100010*20];
void build(int l,int r,int &rt,int num)
{
tree[con++]=tree[rt];
rt=con-1;
tree[rt].sum++;
if(l==r)
{
return;
}
int mid=(l+r)>>1;
if(num<=mid)
build(l,mid,tree[rt].l,num);
else
build(mid+1,r,tree[rt].r,num);
}
int query(int l,int r,int nowq,int nowh,int k)
{
if(l==r)
return l;
int c=tree[tree[nowq].l].sum-tree[tree[nowh].l].sum,mid=(l+r)>>1;
if(k<=c)
query(l,mid,tree[nowq].l,tree[nowh].l,k);
else
query(mid+1,r,tree[nowq].r,tree[nowh].r,k-c);
}
int main()
{
// freopen("1.txt","r",stdin);
int m;
cin>>n>>m;
rt[0]=0;
tree[0].l=tree[0].r=tree[0].sum=0;
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i]);
p[i]=num[i];
}
sort(p+1,p+n+1);
int ed=unique(p+1,p+n+1)-p;
for(int i=1;i<=n;++i)
{
int mid=lower_bound(p+1,p+ed,num[i])-p;
rt[i]=rt[i-1];
build(1,n,rt[i],mid);
}
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
// cout<<query(1,n,rt[b],rt[a-1],c)-1<<endl;
printf("%d\n",p[query(1,n,rt[b],rt[a-1],c)]);
}
/* for(int i=0;i<=n;++i)
cout<<rt[i]<<endl;*/
return 0;
}
带修主席树
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
struct node
{
int l,r,v;
}tree[N*400];
int rt[N],con=1,dis[N*2],num[N],n,q[N],h[N],iq,ih,cnt=0,ed;
struct OP
{
int a,b,c,d;
}op[N];
inline int lowbit(int &x)
{return x&(-x);}
void add(int l,int r,int &now,int x,int z)
{
if(!now)
now=con++;
tree[now].v+=z;
if(l==r)
return;
int mid=(l+r)>>1;
if(x<=mid)
add(l,mid,tree[now].l,x,z);
else
add(mid+1,r,tree[now].r,x,z);
}
void change(int x,int y,int z)
{
while(x<=n)
{
add(1,ed-1,rt[x],y,z);
x+=lowbit(x);
}
}
int sc(int l,int r,int k)
{
if(l==r)
return l;
int mid=(l+r)>>1;
int s1=0,s2=0;
for(int i=0;i<iq;++i)
s1+=tree[tree[q[i]].l].v;
for(int i=0;i<ih;++i)
s2+=tree[tree[h[i]].l].v;
// cout<<l<<' '<<mid<<' '<<s2-s1<<' '<<k<<endl;
if(s2-s1>=k)
{
for(int i=0;i<iq;++i)
q[i]=tree[q[i]].l;
for(int i=0;i<ih;++i)
h[i]=tree[h[i]].l;
return sc(l,mid,k);
}
else
{
for(int i=0;i<iq;++i)
q[i]=tree[q[i]].r;
for(int i=0;i<ih;++i)
h[i]=tree[h[i]].r;
return sc(mid+1,r,k-(s2-s1));
}
}
int query(int l,int r,int k)
{
iq=ih=0;
while(l>=1)
{
q[iq++]=rt[l];
l-=lowbit(l);
}
while(r>=1)
{
h[ih++]=rt[r];
r-=lowbit(r);
}
return sc(1,ed-1,k);
}
int main()
{
// freopen("1.txt","r",stdin);
int m;cin>>n>>m;
for(int i=1;i<=n;++i)
{
scanf("%d",&num[i]);
dis[i]=num[i];
}
cnt=n+1;
for(int i=0;i<m;++i)
{
char s[12];
scanf("%s",s);
if(s[0]=='Q')
{
scanf("%d%d%d",&op[i].a,&op[i].b,&op[i].c);
op[i].d=1;
}
else
{
scanf("%d%d",&op[i].a,&op[i].b);
op[i].d=0;
dis[cnt++]=op[i].b;
}
}
sort(dis+1,dis+cnt);
ed=unique(dis+1,dis+cnt)-dis;
for(int i=1;i<=n;++i)
{
int tmp=lower_bound(dis+1,dis+ed,num[i])-dis;
num[i]=tmp;
change(i,tmp,1);
}
for(int i=0;i<m;++i)
{
if(op[i].d==1)
{
printf("%d\n",dis[query(op[i].a-1,op[i].b,op[i].c)]);
}
else
{
change(op[i].a,num[op[i].a],-1);
int tmp=lower_bound(dis+1,dis+ed,op[i].b)-dis;
change(op[i].a,tmp,1);
num[op[i].a]=tmp;
}
}
return 0;
}
1064

被折叠的 条评论
为什么被折叠?



