其他分享
首页 > 其他分享> > BJOI2016 回转寿司

BJOI2016 回转寿司

作者:互联网

题目链接

Description

给定一个长度为 \(N\) 的序列 \(a\),和一个区间 \([L, R]\)。

求多少连续子序列的权值和在区间内,即满足 \(1 \le i \le j \le n\) 且满足 \(L \le \sum_{k=i}^{j} a[i] \le R\) 的方案数。

Solution

区间和,很容易想到用前缀和转换,这样区间相关变成了两个点。设 \(s\) 为 \(a\) 的前缀和,那么统计就变成了这样。

统计 \(0 \le i < j \le n\) 中满足 \(L \le s[j] - s[i] \le R\) 的方案数的。数据只有一组询问,显然是支持我们枚举一维,的不妨枚举 \(s[i]\),那么转化一下式子,就是满足 \(L + s[i] \le s[j] \le R + s[i]\) 且 \(i < j\) 的 \(j\) 的数量。

这就是一个显然的二维偏序问题,做法就是:

单调修改、区间查询这个操作我们再熟悉不过了。但是这次因为离散化会把值域信息搞没,所以不能离散化,只能动态开点线段树。(后来想了一下好像也可以,把数值全部打进数组离散化一下,所以写了两个版本)。

时间复杂度

\(O(n\log_2 10^{10})\)

Code

动态开点线段树版

#include <iostream>
#include <cstdio>

using namespace std;

typedef long long LL;

const int N = 100005;

int n, L, R, rt, idx;

struct T{
	int l, r, v;
} t[N * 30];

LL Lt = 9e18, Rt = -9e18;

LL s[N], ans = 0;

void inline pushup(int p) {
	t[p].v = t[t[p].l].v + t[t[p].r].v;
}

void insert(int &p, LL l, LL r, LL x) {
	if (!p) p = ++idx;
	t[p].v++;
	if (l == r) return;
	LL mid = (l + r) >> 1;
	if (x <= mid) insert(t[p].l, l, mid, x);
	else insert(t[p].r, mid + 1, r, x);
}

int query(int p, LL l, LL r, LL x, LL y) {
	if (!p) return 0;
	if (x <= l && r <= y) return t[p].v;
	LL mid = (l + r) >> 1, res = 0;
	if (x <= mid) res += query(t[p].l, l, mid, x, y);
	if (mid < y) res += query(t[p].r, mid + 1, r, x, y);
	return res;
}

int main() {
	scanf("%d%d%d", &n, &L, &R);
	for (int i = 1; i <= n; i++) scanf("%lld", &s[i]), s[i] += s[i - 1];
	for (int i = 1; i <= n; i++) {
		Lt = min(Lt, s[i]);
		Rt = max(Rt, R + s[i]);
	}
	for (int i = n; ~i; i--) {
		ans += query(rt, Lt, Rt, L + s[i], R + s[i]);
		if (i) insert(rt, Lt, Rt, s[i]);
	}
	printf("%lld\n", ans);
	return 0;
}

树状数组版

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;

const int N = 100005;

int n, L, R, tot, c[N];

LL s[N], d[N], ans = 0;

int inline get(LL x) {
	return lower_bound(d + 1, d + 1 + tot, x) - d;
}

void inline add(int x) {
	for (; x <= tot; x += x & -x) c[x]++;
}

int inline ask(int x) {
	int res = 0;
	for (; x; x -= x & -x) res += c[x];
	return res;
}

int main() {
	scanf("%d%d%d", &n, &L, &R);
	for (int i = 1; i <= n; i++) 
		scanf("%lld", &s[i]), s[i] += s[i - 1], d[++tot] = s[i];
	sort(d + 1, d + 1 + tot);
	tot = unique(d + 1, d + 1 + tot) - d - 1;
	for (int i = n; ~i; i--) {
		int A = lower_bound(d + 1, d + 1 + tot, L + s[i]) - d - 1; 
		int B = upper_bound(d + 1, d + 1 + tot, R + s[i]) - d - 1; 
		ans += ask(B) - ask(A);
		if (i) add(get(s[i]));
	}
	printf("%lld\n", ans);
	return 0;
}

标签:le,寿司,int,LL,long,BJOI2016,回转,include,void
来源: https://www.cnblogs.com/dmoransky/p/12602313.html