【BZOJ4408】[FJOI2016]神秘数(主席树)

【BZOJ4408】[FJOI2016]神秘数(主席树)

题面

BZOJ
洛谷

题解

考虑只有一次询问。
我们把所有数排个序,假设当前可以表示出的最大数是\(x\)
起始\(x=0\)
依次考虑接下来的每个数\(a_i\),如果\(a_i\le x\),那么没有啥问题,\(x+=a_i\)
如果\(a_i=x+1\),那么也没有问题,\(x+=a_i\)
如果\(a_i>x\),那么\(x+1\)就拼不出来了。
那么显然考虑每次询问,首先把所有\(\le x\)的数全部加进来,然后考虑下一个比\(x\)大的数,如果其大于\(x+1\),那么直接就\(GG\)了。
否则等于\(x+1\),意味着\(x\)至少要翻一倍。
因此最多执行\(log\)次翻倍的操作。
抄起主席树暴力维护就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int ls,rs,v;}t[MAX*35];int tot;
void Modify(int &x,int l,int r,int p)
{
	t[++tot]=t[x];x=tot;t[x].v+=p;
	if(l==r)return;int mid=(l+r)>>1;
	if(p<=mid)Modify(t[x].ls,l,mid,p);
	else Modify(t[x].rs,mid+1,r,p);
}
int Query(int x,int y,int l,int r,int p)
{
	if(l==r)return t[x].v-t[y].v;int mid=(l+r)>>1;
	if(p<=mid)return Query(t[x].ls,t[y].ls,l,mid,p);
	else return Query(t[x].rs,t[y].rs,mid+1,r,p)+t[t[x].ls].v-t[t[y].ls].v;
}
int n,m,a[MAX],rt[MAX];
int main()
{
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int i=1;i<=n;++i)Modify(rt[i]=rt[i-1],1,1e9,a[i]);
	m=read();
	while(m--)
	{
		int l=read(),r=read(),x=0;
		while(x<1e9)
		{
			int v=Query(rt[r],rt[l-1],1,1e9,x+1);
			if(v==x)break;x=v;
		}
		printf("%d\n",x+1);
	}
	return 0;
}
posted @ 2019-03-07 14:57  小蒟蒻yyb  阅读(340)  评论(0编辑  收藏  举报