BZOJ 4408: [Fjoi 2016]神秘数

BZOJ 4408: [Fjoi 2016]神秘数

标签(空格分隔): OI-BZOJ OI-可持久化线段树


Time Limit: 10 Sec
Memory Limit: 128 MB


Description

一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数。例如S={1,1,1,4,13},

1 = 1

2 = 1+1

3 = 1+1+1

4 = 4

5 = 4+1

6 = 4+1+1

7 = 4+1+1+1

8无法表示为集合S的子集的和,故集合S的神秘数为8。

现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间l,r,求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数。

Input

第一行一个整数n,表示数字个数。
第二行n个整数,从1编号。
第三行一个整数m,表示询问个数。
以下m行,每行一对整数l,r,表示一个询问。

Output

对于每个询问,输出一行对应的答案。

Sample Input

5

1 2 4 9 10

5

1 1

1 2

1 3

1 4

1 5
Sample Output

2

4

8

8

8
HINT

对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9


Solution####

FJOI今年的原题。
求神秘数:设[1,x]都可以被表示,那么加入一个数字a可以发现[1+a,x+a]可以被表示,x+1不能被表示的条件是((sumlimits_{a_i<=x+1}a_i)<x+1)迭代即可,会被fibonacci数列卡到极限,(O(nlog_2n*40))


Code####


#include<iostream>
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
int read()
{
	int s=0,f=1;char ch=getchar();
	while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
	return s*f;
}
struct tree
{
	int s,w[2];
}t[3000005];
int n,w,c,np;
int s[100005],ls[100005],st[100005];
void plu(int x,int s,int c,const int num)
{
	int now=++np;t[now]=t[x];
	if(c==-1){t[now].s+=num;return;}
	int p=((s>>c)&1);
	plu(t[now].w[p],s-(p<<c),c-1,num);
	t[now].w[p]=now+1;
	t[now].s=t[t[now].w[0]].s+t[t[now].w[1]].s;
}
int countt(const int x,const int s,const int c)
{
	if(c==-1)return 0;
	int p=((s>>c)&1);
	return countt(t[x].w[p],s-(p<<c),c-1)+(p?t[t[x].w[0]].s:0);
}
int count(int x,int y,int s,int c)
{
	return countt(x,s,c)-countt(y,s,c);
}
int ef(int ans)
{
	int l=1,r=n+1;ls[r]=2000000000;
	while(l^r)
	  {int mid=l+r>>1;
	   if(ls[mid]>ans)
	     r=mid;
	   else
	     l=mid+1;
	  }
	return l;
}
int main()
{
	//freopen("mystic.in","r",stdin);
	//freopen("mystic.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	   s[i]=ls[i]=read();
	sort(&ls[1],&ls[n+1]);
	for(int i=1;i<=n;i++)
	   s[i]=lower_bound(&ls[1],&ls[n+1],s[i])-ls;
	for(w=1,c=0;w<=n;w*=2,c++);np=1;st[0]=1;
	for(int i=1;i<=n;i++)
	   st[i]=np+1,
	   plu(st[i-1],s[i],c-1,ls[s[i]]);
	for(int m=read();m--;)
	   {
			int l=read(),r=read();
			int ans=1,ss=0;
			while((ss=count(st[r],st[l-1],ef(ans),c-1))>=ans)
			  ans=ss+1;
			printf("%d
",ans);
	   }
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/wuyuhan/p/5242080.html