Bzoj 4408: [Fjoi 2016]神秘数 可持久化线段树,神题

4408: [Fjoi 2016]神秘数

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 177  Solved: 128
[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

鸣谢yyh上传

题解:

FJ神题!!!

这道题实在是迷幻。。。

根本想不到啊!!!QAQ!!!我一直在想数学方法。。。

看了题解,竟然是可持久化线段树!!!!(Orz 出题人)

好了不废话了。。。

先把序列从小到大排序。

假设当前神秘数为ans,则[1,ans-1]一定能用S集合中的数表示。然后如果当前加入一个数字a,则可以分为两类讨论。

1,若a<=ans,则当前可以将区间变长为:[1,ans+a-1]  ,然后神秘数变为ans+a。

2,若a>ans,则区间中有空,ans不变。

然后ans从1开始,每次求小于ans的数的和get,ans变为get+1。

这里用可持久化线段树维护即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define MAXN 100010
 4 int sum[40*MAXN],a[MAXN],root[MAXN],SIZE;
 5 struct node
 6 {
 7     int left,right;
 8 }tree[40*MAXN];
 9 int read()
10 {
11     int s=0,fh=1;char ch=getchar();
12     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)fh=-1;ch=getchar();}
13     while(ch>=‘0‘&&ch<=‘9‘){s=s*10+(ch-‘0‘);ch=getchar();}
14     return s*fh;
15 }
16 void Add(int x,int &y,int l,int r,int add)
17 {
18     y=++SIZE;
19     sum[y]=sum[x]+add;
20     if(l==r)return;
21     tree[y].left=tree[x].left;tree[y].right=tree[x].right;
22     int mid=(l+r)/2;
23     if(add<=mid)Add(tree[x].left,tree[y].left,l,mid,add);
24     else Add(tree[x].right,tree[y].right,mid+1,r,add);
25 }
26 int query(int x,int y,int l,int r,int k)
27 {
28     if(l==r)return sum[y]-sum[x];
29     int mid=(l+r)/2;
30     if(k<=mid)return query(tree[x].left,tree[y].left,l,mid,k);
31     else return query(tree[x].right,tree[y].right,mid+1,r,k)+sum[tree[y].left]-sum[tree[x].left];
32 }
33 int main()
34 {
35     int n,tot,i,m,l,r,ans,get;
36     n=read();
37     tot=0;
38     for(i=1;i<=n;i++)a[i]=read(),tot+=a[i];
39     SIZE=0;
40     for(i=1;i<=n;i++)Add(root[i-1],root[i],1,tot,a[i]);
41     m=read();
42     for(i=1;i<=m;i++)
43     {
44         l=read();r=read();
45         ans=1;
46         while(1)
47         {
48             get=query(root[l-1],root[r],1,tot,ans);
49             if(get<ans)break;
50             ans=get+1;
51         }
52         printf("%d\n",ans);
53     }
54     return 0;
55 }
时间: 2024-12-06 11:49:03

Bzoj 4408: [Fjoi 2016]神秘数 可持久化线段树,神题的相关文章

●BZOJ BZOJ 4408 [Fjoi 2016]神秘数

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4408 题解: 主席树 首先,对于一些数来说, 如果可以我们可以使得其中的某些数能够拼出 1-ret 那么此时的ANS(神秘数)= ret+1 然后考虑,如果此时存在另一个数小于等于 ANS,(设该数为 x) 则一定可以在原来的1-ret的基础上拼出 1-ret+x 即 ANS 可以更新为 ret+x+1 所以具体的操作就是: 每次查询区间内小于ANS的数的和(SUM),然后如果SUM大于A

bzoj 4408: [Fjoi 2016]神秘数

额,一开始突然想到了如果能表示出连续的二进制位,就可以构造出连续的数了..然后想了一下,不可做2333 于是又走上了扒题解的不归路.. 貌似题解就是推广一下?? 如果能表示出[l,r]那么新加入一个数a,那么可以得到一个新的区间是[l+a,r+a],然后和 [l,r]and[l+a,r+a](and表示取并集)就是现在能表示的区间. 现在我们希望 [l,r]and[l+a,r+a]==[l,r+a] ,这样的话考虑a的加入顺序,显然是应该从小到大的. 而且,在[l,r]and[l+a,r+a]=

【BZOJ-4408】神秘数 可持久化线段树

4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 475  Solved: 287[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的子集的

【BZOJ4408】[Fjoi 2016]神秘数 主席树神题

[BZOJ4408][Fjoi 2016]神秘数 Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1+1+14 = 45 = 4+16 = 4+1+17 = 4+1+1+18无法表示为集合S的子集的和,故集合S的神秘数为8.现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数

[POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]

可持久化线段树模板题. #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using namespace std; int n,q,tot,a[110000]; in

[BZOJ 3218] A + B Problem 【可持久化线段树 + 网络流】

题目连接:BZOJ - 3218 题目分析 题目要求将 n 个点染成黑色或白色,那么我们可以转化为一个最小割模型. 我们规定一个点 i 最后属于 S 集表示染成黑色,属于 T 集表示染成白色,那么对于每个点 i 就要连边 (S, i, B[i]) 和 (i, T, W[i]). 这样,如果一个点属于 S 集,就要割掉与 T 相连的边,就相当于失去了染成白色的收益. 我们再来考虑 “奇怪的点”,一个点 i 变成奇怪的点的条件是:i 是黑色且存在一个白色点 j 满足 j < i && L

BZOJ 3932 CQOI 2015 任务查询系统 可持久化线段树

题目大意 给出一些任务开始的时间,结束的时间,和优先级.问在第k秒时的第k大优先级,和前k小优先级的和. 思路 CQOI太良心,所有题都是512M. 这个题只需要按照时间轴弄一个可持久化线段树就行了,每个时间点对应着一个权值线段树,维护子节点的和和个数. 注意在没有操作的时候,当前时间点的线段树要复制上一个时间点的线段树. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #inclu

【bzoj4408】[Fjoi 2016]神秘数 主席树

题目描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1+1+14 = 45 = 4+16 = 4+1+17 = 4+1+1+18无法表示为集合S的子集的和,故集合S的神秘数为8.现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数. 输入 第一行一个整数n,表示数字个数.第二行n个整数,从1编

bzoj4408: [Fjoi 2016]神秘数

题意:给n个数,定义一段区间神秘数为该区间所有数字通过组合相加所能得到的数的mex,m个询问,对于区间[l,r]询问该区间的神秘树. 如果我们将这段数排序,并且已知前n个数的神秘数为x,即现在凑得的数的区间为[1,x],新加入的数为a,那么不难发现,我们凑得的数又得到了一段区间[a+1,a+x],那么如果a+1<=x,我们就可以拼上这两段,而神秘数变为a+x+1. 也即是说,我们有当前解ans,我们将所有小等ans的数加起来(其实根据前面所推应该是小于,但是写小等不会错,而且对于代码来说更好些,