[Luogu P4587] [BZOJ 4408] [FJOI2016]神秘数

43 篇文章 1 订阅
8 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

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

1 = 1 1 = 1 1=1

2 = 1 + 1 2 = 1+1 2=1+1

3 = 1 + 1 + 1 3 = 1+1+1 3=1+1+1

4 = 4 4 = 4 4=4

5 = 4 + 1 5 = 4+1 5=4+1

6 = 4 + 1 + 1 6 = 4+1+1 6=4+1+1

7 = 4 + 1 + 1 + 1 7 = 4+1+1+1 7=4+1+1+1

8 8 8无法表示为集合 S S S的子集的和,故集合 S S S的神秘数为 8 8 8

现给定 n n n个正整数 a [ 1 ] . . . a [ n ] a[1]...a[n] a[1]...a[n] m m m个询问,每次询问给定一个区间 [ l , r ] [l,r] [l,r],求由 a [ l ] , a [ l + 1 ] , … , a [ r ] a[l],a[l+1],…,a[r] a[l],a[l+1],,a[r]所构成的可重复数字集合的神秘数。

输入输出格式

输入格式:

第一行一个整数 n n n,表示数字个数。

第二行 n n n个整数,从 1 1 1编号。

第三行一个整数 m m m,表示询问个数。

以下 m m m行,每行一对整数 l , r l,r l,r,表示一个询问。

输出格式:

对于每个询问,输出一行对应的答案。

输入输出样例

输入样例#1:
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
输出样例#1:
2
4
8
8
8

说明

对于100%的数据点,$n,m \le 100000 0 , 0, 0∑a[i] \le 10^9$

解题分析

这道题的思路来自于一个很显然的结论: 从 1 1 1开始递推, 如果 a n s ≤ ∑ i = 1 a n s n u m [ i ] × i ans\le \sum_{i=1}^{ans}num[i]\times i ansi=1ansnum[i]×i的数凑出来, 那么 ∑ i = 1 a n s n u m [ i ] × i \sum_{i=1}^{ans}num[i]\times i i=1ansnum[i]×i也能凑出来。 这样搞我们用主席树维护, 每次 a n s ans ans至少增大一倍, 总复杂度就是 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))的。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dot, q, cnt, dif;
struct Node {int sum, son[2];} tree[MX * 40];
int root[MX], buf[MX], dat[MX];
namespace SGT
{
	#define ls tree[now].son[0]
	#define rs tree[now].son[1]
	#define pls tree[pre].son[0]
	#define prs tree[pre].son[1]
	void insert(R int pre, int &now, R int lef, R int rig, R int tar, R int val)
	{
		now = ++cnt; tree[now] = tree[pre]; tree[now].sum += val;
		if(lef == rig) return;
		int mid = lef + rig >> 1;
		if(tar <= mid) insert(pls, ls, lef, mid, tar, val);
		else insert(prs, rs, mid + 1, rig, tar, val);
	}
	int query(R int pre, R int now, R int lef, R int rig, R int bd)
	{
		if(rig <= bd) return tree[now].sum - tree[pre].sum;
		int mid = lef + rig >> 1;
		int ret = query(pls, ls, lef, mid, bd);
		if(bd > mid) ret += query(prs, rs, mid + 1, rig, bd);
		return ret;
	}
}
IN int getid(R int now) {int k = std::lower_bound(buf + 1, buf + 1 + dif, now) - buf; if(buf[k] > now) k--; return k;}
int main(void)
{
	int a, b, ans, pos, sum;
	in(dot);
	for (R int i = 1; i <= dot; ++i) in(dat[i]), buf[i] = dat[i];
	std::sort(buf + 1, buf + 1 + dot); dif = std::unique(buf + 1, buf + 1 + dot) - buf - 1;
	for (R int i = 1; i <= dot; ++i) SGT::insert(root[i - 1], root[i], 1, dif, getid(dat[i]), dat[i]);
	in(q);
	W(q--)
	{
		in(a), in(b);
		ans = 1;
		W(233)
		{
			sum = SGT::query(root[a - 1], root[b], 1, dif, getid(ans));
			if(sum >= ans) ans = sum + 1;
			else break;
		}
		printf("%d\n", ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值