#41. 新斯诺克

Description

内部题目,不放链接了。

Solution

题意大概就是让求平均数大于 \(m\) 的子段的个数。

很容易想到前缀和,但是这个前缀和该怎么用呢?

我们可以先把 \(a\) 序列每个数都减去 \(m\) 之后在做前缀和。

这样判断一个子段是否合法只需要判断子段和是否大于 0,即可。

暴力枚举的话是 \(O(n^2)\) 的,无法通过此题。

仔细分析我们需要的条件 \(sum_i - sum_j > 0 \ (i > j)\)

诶,这不就是个二维正序嘛。

于是拿个树状数组维护即可。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long

using namespace std;

const ll N = 1e6 + 10;
ll n, m, ans;
ll c[N];
struct node{
	ll a, b;
	bool operator < (const node &t) const{
		return a != t.a ? a < t.a : b > t.b;
	}
}p[N];

inline void update(ll x, ll y){
	for(; x <= n; x += (x & -x))
		c[x] += y;
}

inline ll query(ll x){
	ll res = 0;
	for(; x > 0; x -= (x & -x))
		res += c[x];
	return res;
}

signed main(){
	scanf("%lld%lld", &n, &m);
	for(ll i = 1; i <= n; i++){
		scanf("%lld", &p[i].a);
		p[i].a = p[i - 1].a + p[i].a - m;
		ans += (p[i].a > 0);
		p[i].b = i;
	}
	sort(p + 1, p + 1 + n);
	for(ll i = 1; i <= n; i++){
		update(p[i].b, 1);
		ans += query(p[i].b - 1);
	}
	printf("%lld\n", ans);
	return 0;
}

End

posted @ 2021-09-25 23:29  xixike  阅读(40)  评论(0编辑  收藏  举报