Loading

P9681 幽默的世界。 题解

题目传送门

这个人想不出普及组做法。。。

Solution

不难发现符合题目要求的序列是最后一个数为正数,前面的数都为负数,且全部加起来大于等于 0。

考虑对于每一个右端点,都求出他往前能到的左端点,满足这一段的和大于 0 且只有右端点是正数。

显然前面一段负数的前缀和有单调性,直接二分就可以求。

for(int i = 1; i <= n; i ++) {
	cin >> a[i];
	sum[i] = sum[i - 1] + a[i];
	if(a[i] <= 0) cnt ++;
	else {
		int l = 1, r = cnt + 1, tmp = 1;
		while(l <= r) {
			int mid = l + r >> 1;
			if(sum[i] - sum[i - mid] > 0) l = mid + 1, tmp = mid;
			else r = mid - 1;
		}
		ls[i] = tmp;
		cnt = 0;
	}
}

然后就可以很简单的写出一段 35 分代码。

while(q --) {
	int l, r;
	int res = 0;
	cin >> l >> r;
	for(int i = l; i <= r; i ++) {
		if(a[i] >= 0)
			res += min(i - l + 1, ls[i]);
	}
	cout << res << '\n';
}

这个式子看起来还不是很好直接优化的样子,我们考虑换一个形式。

for(int i = 1; i <= n; i ++) 
	p[i] = i - ls[i] + 1;
while(q --) {
	int l, r;
	int res = 0;
	cin >> l >> r;
	for(int i = l; i <= r; i ++) 
		res += i + 1 - max(p[i], l);
	cout << res << '\n';
}

前面 \(\sum_{i=l}^{i=r} i + 1\) 是好算的,只需要求后面这段就好。

求后面这个需要用到的有:

  1. 区间 \([l, r]\) 中大于等于 \(l\) 的个数。
  2. 区间 \([l, r]\) 中大于等于 \(l\) 的数之和。

直接上主席树就可以做了。

#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 2e5 + 5, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, q;
int a[N], ls[N], s[N], p[N];
LL sum[N];
struct SegT {
	int l, r;
	PII val;
	#define l(x) tr[x].l
	#define r(x) tr[x].r
	#define val(x) tr[x].val
} tr[N << 5];
int rt[N], idx;
void pushup(int x) {
	val(x).fi = val(l(x)).fi + val(r(x)).fi;
	val(x).se = val(l(x)).se + val(r(x)).se;
}
void build(int &x, int l, int r) {
	x = ++ idx;
	tr[x] = {l, r};
	if(l == r) return;
	int mid = l + r >> 1;
	build(l(x), l, mid), build(r(x), mid + 1, r);
}
void insert(int &x, int lst, int l, int r, int v) {
	x = ++ idx;
	tr[x] = tr[lst];
	if(l == r) {
		val(x).fi += v;
		val(x).se ++;
		return;
	}
	int mid = l + r >> 1;
	if(v <= mid) insert(l(x), l(lst), l, mid, v);
	else insert(r(x), r(lst), mid + 1, r, v);
	pushup(x);
}
PII query(int x, int y, int l, int r, int nl, int nr) {
	if(nl <= l && r <= nr) {
		return {val(y).fi - val(x).fi, val(y).se - val(x).se};
	}
	int mid = l + r >> 1;
	PII res;
	if(nl <= mid) res = query(l(x), l(y), l, mid, nl, nr);
	if(nr > mid) {
		auto t = query(r(x), r(y), mid + 1, r, nl, nr);
		res.fi += t.fi;
		res.se += t.se;
	}
	return res;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
	cin >> n >> q;
	int cnt = 0;
	for(int i = 1; i <= n; i ++) {
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
		if(a[i] <= 0) cnt ++;
		else {
			int l = 1, r = cnt + 1, tmp = 1;
			while(l <= r) {
				int mid = l + r >> 1;
				if(sum[i] - sum[i - mid] > 0) l = mid + 1, tmp = mid;
				else r = mid - 1;
			}
			ls[i] = tmp;
			cnt = 0;
		}
	}
	for(int i = 1; i <= n; i ++) 
		s[i] = s[i - 1] + ls[i], p[i] = i - ls[i] + 1;
		
	build(rt[0], 1, n);
	for(int i = 1; i <= n; i ++)
		insert(rt[i], rt[i - 1], 1, n, p[i]);
	while(q --) {
		int l, r;
		LL res = 0;
		cin >> l >> r;
		if(l == 1) cout << s[r] - s[l - 1] << '\n';
		else {
			auto t = query(rt[l - 1], rt[r], 1, n, l, n);
			res = (l + r) * (r - l + 1) / 2 + (r - l + 1);
			res -= t.fi;
			res -= (r - l + 1 - t.se) * l;
			cout << res << '\n';
		}
	}
    return 0;
}
posted @ 2023-10-24 08:24  Svemit  阅读(26)  评论(0)    收藏  举报