jzoj3379-查询【主席树】

正题


题目大意

给出一个有序集合AAA,定义Al,rA_{l,r}Al,r​表示集合内lrl\sim rl∼r这个范围内的数。

定义加法A+BA+BA+B表示两个集合中的所有元素(不去重)。

现在询问,每次询问ki,pik_i,p_iki​,pi​然后给出kik_iki​个区间[lj,rj][l_j,r_j][lj​,rj​]
j=1kiAlj,rj\sum_{j=1}^{k_i} A_{l_j,r_j}j=1∑ki​​Alj​,rj​​
这个集合中第pip_ipi​小的数。


解题思路

因为k5k\leq 5k≤5所以我们可以考虑一般的主席树求区间第kkk小。

现在对于每个询问的lj,rjl_j,r_jlj​,rj​我们让kkk组下标同时在主席数上跑动。那么每次的数字个数即是kkk组下标计算出来的数字的和。然后往左或往右区间走动是让555组区间同时走动即可。


codecodecode

#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)]);
    }
}
上一篇:PHP ftp_nb_get() 函数


下一篇:在SContruct中编译.c