BZOJ 4408: [Fjoi 2016]神秘数

4408: [Fjoi 2016]神秘数

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 553  Solved: 343
[Submit][Status][Discuss]

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](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

 

Source

分析:

很机智的想法...貌似之前见过类似的想法...然而并没有想到..QAQ~~~

考虑当前我们有一个数字集合可以表示区间$[1,x]$内的数字,考虑新加入一个数字$y$,分以下两种情况:

$1.$  $y<=x+1$,那么我们发现新的数字集合可以表示区间$[1,x+y]$内的所有数字

$2.$  $y>x+1$,那么$x+1$这个数字不能被表示出来,所以$x+1$就是答案...

所以对于每个区间,我们先选定$ans=1$作为空集的神秘数,也就是说当前的集合可以表示区间$[0,ans-1]$,然后把区间内所有小于等于$ans$的数字全部加起来得到$sum$,如果$sum<ans$,那么就代表不存在新的小于等于$ans$的数字,那么就代表$ans$就是当前区间的神秘数,查询$sum$可以主席树维护...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
#include<map>
using namespace std;

const int maxn=100000+5,maxm=7000000+5;

int n,m,id,tot,a[maxn],mp[maxn],ls[maxm],rs[maxm],val[maxn],root[maxn];

long long ans,tmp,sum[maxm];

inline bool cmp(int x,int y){
	return a[x]<a[y];
}

inline void change(int l,int r,int x,int &y,int pos,int val){
	y=++tot;sum[y]=sum[x]+val;
	if(l==r) return;
	int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
	if(pos<=mid)
		change(l,mid,ls[x],ls[y],pos,val);
	else
		change(mid+1,r,rs[x],rs[y],pos,val);
}

inline long long query(int l,int r,int y,int L,int R){
	if(l==L&&r==R) return sum[y];
	int mid=(l+r)>>1;
	if(R<=mid)
		return query(l,mid,ls[y],L,R);
	else if(L>mid)
		return query(mid+1,r,rs[y],L,R);
	else
		return query(l,mid,ls[y],L,mid)+query(mid+1,r,rs[y],mid+1,R);
}

signed main(void){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[i]=i,val[i]=a[i];
	sort(mp+1,mp+n+1,cmp);sort(val+1,val+n+1);
	for(int i=1;i<=n;i++)
		change(1,n,root[i-1],root[i],mp[i],a[mp[i]]);
	scanf("%d",&m);
	for(int i=1,l,r;i<=m;i++){
		scanf("%d%d",&l,&r);ans=1;
		while(9524){
			id=upper_bound(val+1,val+n+1,ans)-val-1;
			tmp=query(1,n,root[id],l,r);
			if(tmp<ans) break;
			else ans=tmp+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

  


By NeighThorn

 

posted @ 2017-03-31 19:48  NeighThorn  阅读(250)  评论(0编辑  收藏  举报