题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4299

https://lydsy.com/JudgeOnline/problem.php?id=4408

(双倍经验)

题解

考虑如果直接给一个序列要求出它的神秘数应该怎么做。

对于第 \(i\) 个数,如果我们已经有了前 \(i-1\) 个数的神秘数 \(s\),那么也就是说 \([1, s - 1]\) 的正整数全部都是可以组成的。

如果 \(a_i \leq s\) 的话,那么 \([1, s - 1]\) 的数和 \(a_i\) 可以组成 \([a_i + 1, a_i + s - 1]\)。因为 \(a_i \leq s\) 所以 和之前的区间合并起来就是 \([1, a_i + s - 1]\) 所以新的 \(s\) 就是 \(s + a_i\)。

如果 \(a_i > s\),因为 \(a_i\) 无法对目前不能被表示出来的数的大小产生影响,所以 \(s\) 不变。

但是为了防止在第一种情况中的新的 \(s\) 已经被之前的第二种情况中的本来可以被表示出来的数给表示出来了,所以我们可以按照 \(a\) 从小到大的顺序处理。

那么这个时候如果遇到第二种情况其实就可以直接结束了。


考虑这个做法如何支持区间多组询问。

很容易发现,我们最后取的答案一定是把整段区间排序完以后的结果的一个前缀和的值 \(+1\),这个前缀结束的位置应该是这个前缀和 \(+1\) 的值 \(<\) 后面的第一个值的位置。

于是我们可以得到一个思路:

对于目前的前缀和 \(s - 1\),我们可以在这个区间形成的序列中找到大于这个 \(s\) 的最小的数。那么之前的数是一定可以保证 \(\leq s\) 的。然后把 \(s\) 更新为新的前缀和 \(+1\)。直到 \(s\) 不再变化为止。

重复这个过程就可以了。

可以发现 \(s\) 每做一次就会至少变大 \(2\) 倍,所以不会做超过 \(\log n\) 次。


维护的话使用主席树维护,可以方便地查询出来每一个区间的小于等于某个值的数的和。


时间复杂度 \(O(n\log^2n)\)。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
} const int N = 1e5 + 7; int n, m, dis, nod;
int a[N], b[N], rt[N]; struct Node { int lc, rc, val, sum; } t[N * 18]; inline void ins(int &o, int p, int L, int R, int x) {
t[o = ++nod] = t[p], ++t[o].val, t[o].sum += b[x];
if (L == R) return;
int M = (L + R) >> 1;
if (x <= M) ins(t[o].lc, t[p].lc, L, M, x);
else ins(t[o].rc, t[p].rc, M + 1, R, x);
}
inline int qsum(int o, int p, int L, int R, int l, int r) {
if (l > r) return 0;
if (l <= L && R <= r) return t[o].sum - t[p].sum;
int M = (L + R) >> 1;
if (r <= M) return qsum(t[o].lc, t[p].lc, L, M, l, r);
if (l > M) return qsum(t[o].rc, t[p].rc, M + 1, R, l, r);
return qsum(t[o].lc, t[p].lc, L, M, l, r) + qsum(t[o].rc, t[p].rc, M + 1, R, l, r);
} inline int get(int x) { return std::upper_bound(b + 1, b + dis + 1, x) - b - 1; }
inline void lsh() {
std::sort(b + 1, b + n + 1);
dis = std::unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) a[i] = get(a[i]);
} inline void work() {
lsh();
b[++dis] = (1ll << 31) - 1;
for (int i = 1; i <= n; ++i) ins(rt[i], rt[i - 1], 1, dis, a[i]);
read(m);
while (m--) {
int l, r;
read(l), read(r);
if (l > r) std::swap(l, r);
int s = 1, tmp;
while ((tmp = qsum(rt[r], rt[l - 1], 1, dis, 1, get(s))) >= s) s = tmp + 1;
printf("%d\n", s);
}
} inline void init() {
read(n);
for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}

bzoj4408 [Fjoi 2016]神秘数 & bzoj4299 Codechef FRBSUM 主席树+二分+贪心的更多相关文章

  1. [BZOJ4408][Fjoi 2016]神秘数

    [BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...

  2. BZOJ4408: [Fjoi 2016]神秘数【主席树好题】

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

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

    题目链接 BZOJ4408 题解 假如我们已经求出一个集合所能凑出连续数的最大区间\([1,max]\),那么此时答案为\(max + 1\) 那么我们此时加入一个数\(x\),假若\(x > ...

  4. bzoj 4408: [Fjoi 2016]神秘数 数学 可持久化线段树 主席树

    https://www.lydsy.com/JudgeOnline/problem.php?id=4299 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1 ...

  5. BZOJ4299: Codechef FRBSUM(主席树)

    题意 题目链接 数集S的ForbiddenSum定义为无法用S的某个子集(可以为空)的和表示的最小的非负整数. 例如,S={1,1,3,7},则它的子集和中包含0(S’=∅),1(S’={1}),2( ...

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

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

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

    4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 177  Solved: 128[Submit][Status ...

  8. BZOJ 4408: [Fjoi 2016]神秘数

    4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 464  Solved: 281[Submit][Status ...

  9. BZOJ 4408: [Fjoi 2016]神秘数 可持久化线段树

    4408: [Fjoi 2016]神秘数 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4408 Description 一个可重复数字集 ...

随机推荐

  1. VS 2010内存泄漏检测

    控制台程序在启动时调用 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 程序正常退出后会打印内存泄漏信息. MFC程序不用 ...

  2. onload in JavaScript

    https://www.w3schools.com/tags/ev_onload.asp Example Execute a JavaScript immediately after a page h ...

  3. 【转】GLSL资料收集

    https://blog.csdn.net/u013467442/article/details/44457869 其中入门资料相当好:https://blog.csdn.net/racehorse/ ...

  4. C# 内存建表备忘

    #region=====建表===== DataSet dataSet; // 创建表 DataTable table = new DataTable("testTable"); ...

  5. VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 虚拟磁盘数据的传输方式 Transport Methods VixDiskLib_ListTransportModes 枚举支持的传输模式 Vi ...

  6. 测开之路九十四:css之盒子模型

    盒子模型 为了演示方便,把内容放到盒子里面 引用css 演示内容 外边距: 4个方向分开写 简写为一条指令,顺序为上右下左 简写为一条指令,第一个值为上下,第二个值为左右 简写为一条指令,只有一个值时 ...

  7. robot framework :List Variables-List变量及其用法

    [转自:https://blog.csdn.net/yezibang/article/details/52692342] 这一讲我们重点来介绍List Variables-List变量及其用法. 一. ...

  8. IPv4首部

    <图解TCP/IP> 4.7 IPv4的首部 版本:由4比特构成,表示标识IP首部的版本号.IPv4的版本号即为4,因此在这个字段上的值也为“4”. 首部长度:由4比特构成,表明IP首部的 ...

  9. MVC 源码系列之控制器执行(二)

    ## 控制器的执行 上一节说道Controller中的ActionInvoker.InvokeAction public virtual bool InvokeAction(ControllerCon ...

  10. node+express 中安装nodemon实时更新server.js

    每次启动node server.js,有一个缺点,每次server.js文件有改动时,必须重新执行指令node server.js,新的代码才会起作用 解决方案1 全局安装 npm install s ...