【JZOJ3379】查询

description

对于一个整数序列,查询区间第k大数可以在O(logN)的时间内轻松完成。现在我们对这个问题进行推广。

考虑带重复数的集合(multiset)。定义在该类集合上的并操作“+”为两个集合的所有数不剔除重复得到的结果。比如,若A={1,2,2,3},B={2,3,4,4},则C={1,2,2,2,3,3,4,4}。

对于一个给定序列A[1..N],定义A[x..y]为包含y-x+1个元素的集合{A[x],A[x+1],…,A[y]}。现在,给定整数序列A,你需要回答很多询问,其中第i个询问要求集合A[x[i,1]..y[i,1]]+A[x[i,2]..y[i,2]]+…+A[x[i,ki]..y[i,ki]]中第pi小的元素。


analysis

  • 一年的时间使我变成了主席树傻子

  • 一个区间当然可以直接主席树做,五个区间其实也可以

  • \(a\)的值并不是很大,不需要离散化

  • 对于当前查询,可以求出五个区间左儿子区间里面数的个数之和,然后和查询值比较

  • 然后向左或右儿子继续二分,把五个区间也向左或向右挪就好

  • 主席树是真的虚,一定一定要多做点题,巩固老知识,才能更好学习新知识


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 200005
#define MAX 1000005
#define ll long long
#define fo(i,a,b) for (ll i=a;i<=b;++i)
#define fd(i,a,b) for (ll i=a;i>=b;--i)

using namespace std;

ll lson[MAX*20],rson[MAX*20],sum[MAX*20];
ll a[MAXN],root[MAXN];
ll n,m,mx,k,p,tot;
ll L[10],R[10];

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
inline void build(ll &t,ll l,ll r)
{
	t=++tot;
	if (l==r)return;
	ll mid=(l+r)>>1;
	build(lson[t],l,mid),build(rson[t],mid+1,r);
}
inline void modify(ll old,ll &t,ll l,ll r,ll x)
{
	t=++tot;
	lson[t]=lson[old],rson[t]=rson[old],sum[t]=sum[old];
	if (l==r){++sum[t];return;}
	ll mid=(l+r)>>1;
	if (x<=mid)modify(lson[old],lson[t],l,mid,x);
	else modify(rson[old],rson[t],mid+1,r,x);
	sum[t]=sum[lson[t]]+sum[rson[t]];
}
inline ll query(ll l,ll r,ll x)
{
	if (l==r)return l;
	ll mid=(l+r)>>1,tmp=0;
	fo(i,1,k)tmp+=sum[lson[R[i]]]-sum[lson[L[i]]];
	if (x<=tmp)
	{
		fo(i,1,k)L[i]=lson[L[i]],R[i]=lson[R[i]];
		return query(l,mid,x);
	}
	else
	{
		fo(i,1,k)L[i]=rson[L[i]],R[i]=rson[R[i]];
		return query(mid+1,r,x-tmp);
	}
}
int main()
{
	freopen("T3.in","r",stdin);
	n=read(),m=read();
	fo(i,1,n)a[i]=read();
	build(root[0],0,1000000);
	fo(i,1,n)modify(root[i-1],root[i],0,1000000,a[i]);
	while (m--)
	{
		k=read(),p=read();
		fo(i,1,k)L[i]=root[read()-1],R[i]=root[read()];
		printf("%lld\n",query(0,1000000,p));
	}
	return 0;
}
posted @ 2019-07-10 17:24  路人黑的纸巾  阅读(147)  评论(0编辑  收藏  举报