bzoj4408 [Fjoi 2016]神秘数 & bzoj4299 Codechef FRBSUM 主席树+二分+贪心
题目传送门
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 主席树+二分+贪心的更多相关文章
- [BZOJ4408][Fjoi 2016]神秘数
[BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...
- BZOJ4408: [Fjoi 2016]神秘数【主席树好题】
Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13}, 1 = 1 2 = 1+1 3 = 1+1+1 4 = 4 5 = ...
- BZOJ4408 [Fjoi 2016]神秘数 【主席树】
题目链接 BZOJ4408 题解 假如我们已经求出一个集合所能凑出连续数的最大区间\([1,max]\),那么此时答案为\(max + 1\) 那么我们此时加入一个数\(x\),假若\(x > ...
- bzoj 4408: [Fjoi 2016]神秘数 数学 可持久化线段树 主席树
https://www.lydsy.com/JudgeOnline/problem.php?id=4299 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1 ...
- BZOJ4299: Codechef FRBSUM(主席树)
题意 题目链接 数集S的ForbiddenSum定义为无法用S的某个子集(可以为空)的和表示的最小的非负整数. 例如,S={1,1,3,7},则它的子集和中包含0(S’=∅),1(S’={1}),2( ...
- 【BZOJ4408】[Fjoi 2016]神秘数 主席树神题
[BZOJ4408][Fjoi 2016]神秘数 Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1 ...
- Bzoj 4408: [Fjoi 2016]神秘数 可持久化线段树,神题
4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 177 Solved: 128[Submit][Status ...
- BZOJ 4408: [Fjoi 2016]神秘数
4408: [Fjoi 2016]神秘数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 464 Solved: 281[Submit][Status ...
- BZOJ 4408: [Fjoi 2016]神秘数 可持久化线段树
4408: [Fjoi 2016]神秘数 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4408 Description 一个可重复数字集 ...
随机推荐
- k8s上的基础概念和术语
kubernetes基本概念和术语 kubeernetes中的大部分概念如Node,Pod,Replication Controller ,Serverce等都可以看作一种“资源对象”,几乎所有的 ...
- Using Groovy To Import XML Into MongoDB
w https://trishagee.github.io/post/groovy_import_to_mongodb/
- CMD模块打包部署总结
目前线上系统利用Seajs做模板化,但是没有对js和css进行打包,在这次简历搜索优化项目里我尝试对gulp插件对Seajs模块打包. 安装gulp和相关插件 npm install -g gulp ...
- 005-unity3d 添加背景音乐、音效 以及 天空盒子
一.基础知识 1.项目中需要有AudioListener,播放器中播放的声音就是AudioListener组件坐在的位置听到的声音.默认AudioListener是放到Main Camera上.没有A ...
- Map类集合遍历
- 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第1节 Scanner类_4-练习一_键盘输入两个数
导包语句其实不用我们自己去写, 选中后回车会自动的导入包 java.util 如果没有导入进来也可以,光标在关键字那里,ALT+回车 也会自动导入包 运行看一下结果:程序其实还有可以优化的地方 先输入 ...
- delphi 函数isiconic 函数 判断窗口是否最小化
http://blog.sina.com.cn/s/blog_66357ab901012t2h.html delphi 函数isiconic 函数 判断窗口是否最小化 (2012-05-26 22:0 ...
- python 操作openpyxl导出Excel 设置单元格格式以及合并处理
贴上一个例子,里面设计很多用法,根据将相同日期的某些行合并处理. from openpyxl import Workbook from openpyxl.styles import Font, Fil ...
- SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON 什么意思 sql server 2005 2008
原文:http://www.cnblogs.com/ForFreeDom/archive/2009/10/16/1584680.html 在sqlserver2005或SQL2008数据库项目中,创建 ...
- Java对象多态性——对象的转型(引用类型之间的类型转换)
Java引用变量有两个类型:编译时类型和运行时类型.编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定. 若编译时类型和运行时类型不一致,就出现多态 正常的方法调用(本态调用 ...