http://codeforces.com/contest/1042/problem/D
#include <bits/stdc++.h> #define forn(i, n) for (int i = 0; i < int(n); i++) using namespace std; const int N = 200 * 1000 + 13; int n; long long T; int a[N]; int f[N]; void upd(int x){ for (int i = x; i < N; i |= i + 1) ++f[i]; } int get(int x){ int res = 0; for (int i = x; i >= 0; i = (i & (i + 1)) - 1) res += f[i]; return res; } int main() { scanf("%d%lld", &n, &T); forn(i, n) scanf("%d", &a[i]); vector<long long> sums(1, 0ll); long long pr = 0; forn(i, n){ pr += a[i]; sums.push_back(pr); } sort(sums.begin(), sums.end()); sums.resize(unique(sums.begin(), sums.end()) - sums.begin()); long long ans = 0; pr = 0; upd(lower_bound(sums.begin(), sums.end(), 0ll) - sums.begin()); forn(i, n){ pr += a[i]; int npos = upper_bound(sums.begin(), sums.end(), pr - T) - sums.begin(); ans += (i + 1 - get(npos - 1)); int k = lower_bound(sums.begin(), sums.end(), pr) - sums.begin(); upd(k); } printf("%lld ", ans); return 0; }이게 답인데 도저히 왜 펜윅 트리를 쓰는지 모르겠습니다..
forn(i, n){ pr += a[i]; int npos = upper_bound(sums.begin(), sums.end(), pr - T) - sums.begin(); ans += (i + 1 - get(npos - 1)); int k = lower_bound(sums.begin(), sums.end(), pr) - sums.begin(); upd(k); }여기 부분 해석 좀 ㅠㅠ
펜윅 트리를 이용해서 어떤 특정 구간에 있는 수의 개수를 구하는건 유명한 풀이법임. 잘 이해가 되지 않는다면 반전수 구하기를 한번 풀고와봐. Counting Inversion일걸 boj에서
ㄴ.. 으어 단순히 구간합 값을++ 한다는 점에서 구간에 속한 수의 개수를 구하는건 알겠는데 저 식에 어떻게 적용되는지가 문제에요ㅠㅠ
슬랙에서 답변 들었겠지만 모든 구간은 prefix sum 2개의 차로 나타낼 수 있음. 그러면 하나의 prefix를 고정했을때 나머지가 움직이는걸 펜윅으로 빠르게 처리할 수 있다는거야.
ㄴ추가 답변 감사합니당~