@loj - 2174@ 「FJOI2016」神秘数
@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 个正整数 a1 ... an, m 个询问,每次询问给定一个区间 [l, r] (l <= r),求由 al ... ar 所构成的可重复数字集合的神秘数。
输入格式
第一行一个整数 n,表示数字个数。
第二行 n 个整数,从 1 编号。
第三行一个整数 m,表示询问个数。
以下 m 行,每行一对整数 l, r,表示一个询问。
输出格式
对于每个询问,输出一行对应的答案。
样例输入
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
样例输出
2
4
8
8
8
数据范围与提示
对于 100% 的数据点, n, m <= 100000,∑a = 10^9。
@solution@
不妨先看对于一个集合怎么快速求它的 “神秘数”。
众所周知子集和问题是 NPC 的,所以这个 “神秘数” 肯定是具有一定性质的。
我们考虑一个个加入元素。假如加入某一元素 x 时,此时的 “神秘数” 为 k。
如果用上 x 也无法拼出 k,则要么 x > k,要么 k - x 在加入 x 之前也无法被拼出。又因为 k 是最小的,只能说明 x > k。
如果我们从小到大考虑一个集合内的所有元素,且 x > k,则 x 以后也 > k。此时 “神秘数” 就保持不变了。
另一方面,假如 x <= k,则我们的 “神秘数” 将会从 k 变成 k + x。
这样我们就可以 O(n) 从小到大扫一遍,维护一个 “神秘数” k。
注意到 k 始终等于某个前缀的和 + 1,而这给了我们用数据结构维护的机会。
进一步地,假如套到原题上面来,我们可以莫队 + 维护一棵线段树,每个位置维护 \((\sum_{p=1}^{i-1}a_p) + 1 - a_i\)。然后线段树上二分求第一个负数的位置。
但是这还不够。复杂度 \(O(n\sqrt{n}\log n)\) 还是太慢了。
考虑复杂度卡在,我们每一次只能往集合里塞一个数。
假如说每一次当前所有 x <= k 的 x 全部塞进集合,会发生什么。
设前一次的 “神秘数” 为 p,则前一次我们可以把 <= p 的全部塞进集合,得到新一次的 “神秘数” 为 q。
新一轮中,我只能塞 p < x <= q 的数(太小的已经塞过了,太大的塞不进去)。假如能塞,则下一次的 “神秘数” >= 2*p。
于是这种操作下:“神秘数” 呈指数级增长。
那么对于每一个区间 [l, r],我们只需要找到 p < x <= q 的数的求和,得到新的 “神秘数”。
这个是可持久化线段树的经典应用了。我们按 ai 从小到大的顺序往线段树里塞,得到 <= ai 的 n 棵线段树。
@accepted code@
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> pii;
#define mp make_pair
#define fi first
#define se second
const int MAXN = 100000;
struct node{
int sum;
node *ch[2];
}t[40*MAXN + 5], *rt[MAXN + 5], *ncnt, *NIL;
struct segtree{
segtree() {
NIL = ncnt = &t[0];
NIL->ch[0] = NIL->ch[1] = NIL;
NIL->sum = 0;
}
void pushup(node *x) {
x->sum = x->ch[0]->sum + x->ch[1]->sum;
}
node *newnode(const node *pre = NIL) {
ncnt++; *ncnt = *pre;
return ncnt++;
}
node *insert(const node *pre, int l, int r, int p, int k) {
node *x = newnode(pre); x->sum += k;
if( l == r ) return x;
int mid = (l + r) >> 1;
if( p <= mid ) x->ch[0] = insert(pre->ch[0], l, mid, p, k);
else x->ch[1] = insert(pre->ch[1], mid + 1, r, p, k);
pushup(x);
return x;
}
int query(const node *x, int l, int r, int ql, int qr) {
if( ql <= l && r <= qr )
return x->sum;
if( l > qr || r < ql )
return 0;
int mid = (l + r) >> 1;
return query(x->ch[0], l, mid, ql, qr) + query(x->ch[1], mid + 1, r, ql, qr);
}
}T;
int a[MAXN + 5]; pii b[MAXN + 5];
int main() {
int n, m; scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
scanf("%d", &m);
for(int i=1;i<=n;i++)
b[i] = mp(a[i], i);
sort(b + 1, b + n + 1);
rt[0] = NIL;
for(int i=1;i<=n;i++)
rt[i] = T.insert(rt[i - 1], 1, n, b[i].se, b[i].fi);
for(int i=1;i<=m;i++) {
int l, r, nw = 0, x; scanf("%d%d", &l, &r);
while( true ) {
x = T.query(rt[nw], 1, n, l, r) + 1;
int y = upper_bound(b + 1, b + n + 1, mp(x, n + 1)) - b - 1;
if( nw == y ) break;
nw = y;
}
printf("%d\n", x);
}
}
@details@
实现基本没有细节。
所以我太菜了。把正解思路擦边擦了个遍,就是没有想到正解的 “一次加入能加入的所有数”。
这本来是道 sb 题,可惜我太 sb,切不出来。
@loj - 2174@ 「FJOI2016」神秘数的更多相关文章
- 【LOJ】#2174. 「FJOI2016」神秘数
题解 这道题的结论很显然= = 就是暴力求的话,把一个区间的数排一下序,如果当前这个数大于前面所有数的前缀和+1,那么前缀和+1即我们所求的答案 那么我们设置一个当前答案(初始为1),在主席树上求出来 ...
- 「FJOI2016」神秘数 解题报告
「FJOI2016」神秘数 这题不sb,我挺sb的... 我连不带区间的都不会哇 考虑给你一个整数集,如何求这个神秘数 这有点像一个01背包,复杂度和值域有关.但是你发现01背包可以求出更多的东西,就 ...
- loj2174 「FJOI2016」神秘数
先考虑一下一个集合怎么用 \(O(n)\) 时间求出来,然后用主席树推广到一个序列就可以了.大致思想就是考虑一个数的权值和它前面的数的和的关系. #include <algorithm> ...
- LOJ 2172 「FJOI2016」所有公共子序列问题——序列自动机
题目:https://loj.ac/problem/2172 在两个序列自动机上同时走,这样暴搜. 先走字典序小的字符,一边搜一边输出,就是按字典序排序的. 方案数很多,需要高精度?空间很小,要压位. ...
- LOJ 3094 「BJOI2019」删数——角标偏移的线段树
题目:https://loj.ac/problem/3094 弱化版是 AGC017C . 用线段树维护那个题里的序列即可. 对应关系大概是: 真实值的范围是 [ 1-m , n+m ] :考虑设偏移 ...
- 【LOJ】#3094. 「BJOI2019」删数
LOJ#3094. 「BJOI2019」删数 之前做atcoder做到过这个结论结果我忘了... em,就是\([1,n]\)之间每个数\(i\),然后\([i - cnt[i] + 1,i]\)可以 ...
- Loj #3089. 「BJOI2019」奥术神杖
Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...
- Loj #2542. 「PKUWC2018」随机游走
Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
随机推荐
- Java Servlet实现下载文件
一.配置servlet 在WebContent(以前的eclipse版本是WebRoot)文件夹下,有一个web.xml 修改web.xml ,加入以下代码 <servlet> <s ...
- WAP网站的推广方式(自整合篇)
WAP网站推广随着无线互联时代的到来,已经日益受到大家的重视.虽然WAP网站的目前盈利模式还不是很清晰,但WAP网站推广的竞争强度将肯定会越来越激烈,下面和大家一起探讨下WAP网站的推广方法. 方法/ ...
- sar磁盘I/O统计数据
sar是一个研究磁盘I/O的优秀工具.以下是sar磁盘I/O输出的一个示例. 第一行-d显示磁盘I/O信息,5 2选项是间隔和迭代,就像sar数据收集器那样.表3-3列出了字段和说明. 表3-3 ...
- 【洛谷】P1880 石子合并
P1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计 ...
- 【CodeVS】1023 GPA计算
1023 GPA计算 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 小松终于步入了大学的殿堂,带着兴奋和憧憬,他参加了信息科学 ...
- Eclipse 的 Java Web 项目环境搭建
从svn上拉取下来Eclipse的项目 IntelliJ IDEA自动识别到可编译的 src 类目录 Java Web 项目 html(一般命名为:WebRoot) 是整个项目输出的根目录. WEB- ...
- 用Python的pandas框架操作Excel文件中的数据教程
用Python的pandas框架操作Excel文件中的数据教程 本文的目的,是向您展示如何使用pandas 来执行一些常见的Excel任务.有些例子比较琐碎,但我觉得展示这些简单的东西与那些你可以在其 ...
- 【水滴石穿】mobx-todos
我觉得代码在有些程序员手里,就好像是画笔,可以创造很多东西 不要觉得创意少就叫没有创意,每天进步一点点,世界更美好 首先源码地址为:https://github.com/byk04712/mobx-t ...
- 使用split_size优化的ODPS SQL的场景
使用split_size优化的ODPS SQL的场景 首先有两个大背景需要说明如下:说明1:split_size,设定一个map的最大数据输入量,单位M,默认256M.用户可以通过控制这个变量,从而达 ...
- sping,springMVC @Component 注解的对象都是单例模式,变量不能全局
错误方式: 将属性和变量定义为全局,单例模式,所有人共享,导致所有人的数据都发生错误! 正确方式 一: 将变量定义到局部,互不影响. 正确方式 二: 假如必须放到全局所有方法 ...