博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj 4408: [Fjoi 2016]神秘数
阅读量:7239 次
发布时间:2019-06-29

本文共 2561 字,大约阅读时间需要 8 分钟。

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 188  Solved: 137
[][][]

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

题解:

  先考虑答案是怎么来的,把一个区间单独抽出来,把其中的数字排序,设排序之后是a1,a2,a3,a4,a5,a6,让数字自由组合如果出现中断的地方,则这里就是神秘数。

  一句话来说,找到最小的一个神秘数相当于找到一个数x,使得小于x的数字加起来的和小于x。举例来说:1 2 4 9 中数字8保证所有比8小的数字加和:1+2+4=7<8 ,所以8是最小的神秘数。

  现在考虑怎么求这个数,由于是区间询问,所以可以想到可持久化线段树,每棵树都维护着1~MAX,然后就可以找到[l,r]里比x小的数的加和啦。

  再考虑统计答案,答案有可能是1~sum,枚举答案显然不行。如果现在有1~x+1的数字加起来是y,如果y大于x,也即y大于等于x,说明x不是神秘数

可以让x更新为y,比如1 2 4 8 小于9的数字的加和为15,则1~15里的数字都可以表示出来,这可以由第二段的讲解看出来。然后可以用一个while()来表示了。

1 /************************************************************** 2     Problem: 4408 3     User: __abcdef__ 4     Language: C++ 5     Result: Accepted 6     Time:1948 ms 7     Memory:37212 kb 8 ****************************************************************/ 9  10 #include
11 #include
12 #include
13 #include
14 #include
15 #include
16 #include
17 #include
18 using namespace std;19 typedef long long LL;20 const int maxn=100010;21 int N,M,MAX,SUM,L,R,a[maxn];22 struct Tree{23 int lc,rc,sum;24 }T[3000000];25 int root[maxn],siz;26 inline void insert(int &i,int l,int r,int w){27 T[++siz]=T[i]; i=siz; T[i].sum+=w;28 if(l==r) return ;29 int mid=(l+r)>>1;30 if(w<=mid) insert(T[i].lc,l,mid,w);31 else insert(T[i].rc,mid+1,r,w);32 }33 inline int query(int x,int y,int l,int r,int ll,int rr){34 if(ll<=l&&r<=rr) return T[y].sum-T[x].sum;35 int mid=(l+r)>>1;36 int ans=0;37 if(ll<=mid) ans+=query(T[x].lc,T[y].lc,l,mid,ll,rr);38 if(mid+1<=rr) ans+=query(T[x].rc,T[y].rc,mid+1,r,ll,rr);39 return ans;40 }41 int main(){42 scanf("%d",&N);43 for(int i=1;i<=N;i++){44 scanf("%d",&a[i]);45 MAX=max(MAX,a[i]);46 }47 for(int i=1;i<=N;i++){48 root[i]=root[i-1];49 insert(root[i],1,MAX,a[i]);50 } 51 scanf("%d",&M);52 while(M--){53 scanf("%d%d",&L,&R);54 int nowsum=0,tmpsum=0;55 while(true){56 tmpsum=query(root[L-1],root[R],1,MAX,1,nowsum+1);57 if(tmpsum>nowsum) nowsum=tmpsum;58 else break;59 }60 printf("%d\n",nowsum+1);61 }62 return 0;63 }

 

 

 

转载于:https://www.cnblogs.com/CXCXCXC/p/5315774.html

你可能感兴趣的文章
计算字符串最后一个单词的长度,单词以空格隔开。 java算法
查看>>
妙味css3课程---1-1、css中自定义属性可以用属性选择器么
查看>>
MySQL数据库实现Oracle常用函数
查看>>
Python 进阶_OOP 面向对象编程_类属性和方法
查看>>
python 小笔记
查看>>
python firebase
查看>>
MegaCli64 raid对应关系
查看>>
LSM Tree解析
查看>>
js控制css
查看>>
selenium 速查手册 python版
查看>>
第六次JAVA作业
查看>>
关于网友博客中代码的可用性问题
查看>>
Binary Tree Traversals
查看>>
安装 Node.js
查看>>
滚动条的控制
查看>>
glibc库函数,系统调用API
查看>>
【kruscal】【最小生成树】【离线】洛谷 P2266 爱的距离
查看>>
mysql定时备份
查看>>
c++ opencv 动态内存
查看>>
HDU 1075 - What Are You Talking About
查看>>