じょえちゃんえるから。
「平均値が 以上」という条件を見たときにパッと考えつく話がある。
問題概要
個の正の整数列 と整数 が与えられる。
整数列の連続する部分列であって、その平均値が 以上であるものが何個あるかを求めよ。
制約
考えたこと
一般に、
の平均値が 以上
⇔ の平均値が 以上
⇔ の総和が 以上
という言い換えができる。「平均値」に関する条件が、なんと「総和」に関する条件へと早変わりして、滅茶苦茶扱いやすくなるのだ。というわけで、元の問題は次の問題へと言い換えることができる。
個の整数 の連続する部分列であって、その総和が 以上であるものが何個あるかを求めよ。
累積和へ
次に「連続する部分列の総和」と言われたら、累積和が使える。 の累積和を とする。そうすると、元の問題はさらに次のように言い換えられる。
個の整数 に対して、
- (⇔ )
が成立するような の組が何個あるかを求めよ。
これはもう、ほとんど転倒数と一緒だ。
転倒数へ
転倒数は
を満たすような の組の個数である。今回は不等号は逆だけど、求め方はほとんど一緒。BIT を使うといい感じにできる!
転倒数の求め方は、
などに詳しく書いてある!!計算量は 。
コード
#include <bits/stdc++.h> using namespace std; template <class Abel> struct BIT { const Abel UNITY_SUM = 0; // to be set vector<Abel> dat; /* [1, n] */ BIT(int n) : dat(n + 1, UNITY_SUM) { } void init(int n) { dat.assign(n + 1, UNITY_SUM); } /* a is 1-indexed */ inline void add(int a, Abel x) { for (int i = a; i < (int)dat.size(); i += i & -i) dat[i] = dat[i] + x; } /* [1, a], a is 1-indexed */ inline Abel sum(int a) { Abel res = UNITY_SUM; for (int i = a; i > 0; i -= i & -i) res = res + dat[i]; return res; } /* [a, b), a and b are 1-indexed */ inline Abel sum(int a, int b) { return sum(b - 1) - sum(a - 1); } /* debug */ void print() { for (int i = 1; i < (int)dat.size(); ++i) cout << sum(i, i + 1) << ","; cout << endl; } }; int main() { int N; long long K; cin >> N >> K; vector<long long> S(N+1, 0); for (int i = 0; i < N; ++i) { long long a; cin >> a; S[i+1] = S[i] + (a - K); } vector<long long> SS = S; sort(SS.begin(), SS.end()); SS.erase(unique(SS.begin(), SS.end()), SS.end()); long long res = 0; BIT<long long> bit(N+10); for (int i = 0; i <= N; ++i) { int id = lower_bound(SS.begin(), SS.end(), S[i]) - SS.begin(); res += bit.sum(id+1); bit.add(id+1, 1); } cout << res << endl; }