jzoj3379-查询【主席树】
作者:互联网
正题
题目大意
给出一个有序集合A,定义Al,r表示集合内l∼r这个范围内的数。
定义加法A+B表示两个集合中的所有元素(不去重)。
现在询问,每次询问ki,pi然后给出ki个区间[lj,rj]
求j=1∑kiAlj,rj
这个集合中第pi小的数。
解题思路
因为k≤5所以我们可以考虑一般的主席树求区间第k小。
现在对于每个询问的lj,rj我们让k组下标同时在主席数上跑动。那么每次的数字个数即是k组下标计算出来的数字的和。然后往左或往右区间走动是让5组区间同时走动即可。
code
#include<cstdio>
#include<algorithm>
#define MN 201000
using namespace std;
struct tnode{
int w,l,r,ls,rs;
}t[MN<<5];
int n,m,x,y,k,a[MN],b[MN],root[MN],q,w,r[6],l[6],qnum,cnt;
int build(int l,int r)
{
int k=++cnt;
t[k].l=l,t[k].r=r;
if (l==r) return k;
int mid=(l+r)>>1;
t[k].ls=build(l,mid);
t[k].rs=build(mid+1,r);
return k;
}
int addt(int k,int z)
{
int nb=++cnt;
t[nb]=t[k];t[nb].w++;
if (t[k].l==z&&t[k].r==z) return nb;
int mid=(t[k].l+t[k].r)>>1;
if (z<=mid) t[nb].ls=addt(t[k].ls,z);
else t[nb].rs=addt(t[k].rs,z);
return nb;
}
int query(int k)
{
if (t[r[1]].l==t[r[1]].r) return t[r[1]].l;
int num=0;
for(int i=1;i<=qnum;i++)
num+=t[t[r[i]].ls].w-t[t[l[i]].ls].w;
if(k<=num){
for(int i=1;i<=qnum;i++)
r[i]=t[r[i]].ls,l[i]=t[l[i]].ls;
return query(k);
}
else{
for(int i=1;i<=qnum;i++)
r[i]=t[r[i]].rs,l[i]=t[l[i]].rs;
return query(k-num);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
int q=unique(b+1,b+1+n)-b-1;
root[0]=build(1,q);
for (int i=1;i<=n;i++)
{
int te=lower_bound(b+1,b+1+q,a[i])-b;
root[i]=addt(root[i-1],te);
}
for (int i=1;i<=m;i++)
{
scanf("%d%d",&qnum,&k);
for(int j=1;j<=qnum;j++)
scanf("%d%d",&l[j],&r[j]),l[j]=root[l[j]-1],r[j]=root[r[j]];
printf("%d\n",b[query(k)]);
}
}
标签:int,nb,lj,mid,查询,jzoj3379,主席,kkk,rj 来源: https://blog.csdn.net/Mr_wuyongcong/article/details/95351300