codeforces 1042D - Petya and Array【树状数组+离散化】
题目:戳这里
题意:有n个数,问有多少个区间满足[L,R]内的和小于t。
解题思路:
[L,R]内的和小于t等价于sum[R]-sum[L-1]<t,将sum[L-1]左移,可以看出R与L的关系sum[R]<sum[L-1]+t。
因为n个数有正有负,所以前缀和sum[]没法直接二分,需要构造出一个有序的前缀和。这样就可以想到用树状数组来维护前缀和,考虑到树状数组维护前缀和,将R和L的关系改为sum[R]-t<sum[L-1]更好写一些(个人习惯,固定r,二分出l)。然后就转化成了常规的离散化树状数组的题目:遍历R,二分查找l,加入答案,更新树状数组。
附ac代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int inf = 0x3f3f3f3f; 5 const int maxn = 2e5 + 10; 6 #define lowbit(x) x&-x 7 ll sum[maxn]; 8 ll a[maxn]; 9 ll c[maxn]; 10 ll tem[maxn]; 11 ll n, t; 12 void add(ll x, ll u) { 13 while(x <= n) { 14 c[x] += u; 15 x += lowbit(x); 16 } 17 } 18 ll getsum(ll x) { 19 ll res = 0; 20 while(x) { 21 res += c[x]; 22 x -= lowbit(x); 23 } 24 return res; 25 } 26 int main() { 27 ll ans = 0; 28 scanf("%lld %lld", &n, &t); 29 for(ll i = 1; i <= n; ++i) { 30 scanf("%lld", &a[i]); 31 sum[i] = sum[i - 1] + a[i]; 32 if(sum[i] < t) ++ans; 33 } 34 for(ll i = 1; i <= n; ++i) { 35 tem[i] = sum[i]; 36 } 37 sort(tem + 1, tem + 1 + n); 38 for(ll i = 1; i <= n; ++i) { 39 ll x = lower_bound(tem + 1, tem + 1 + n, sum[i]) - tem; 40 ll y = upper_bound(tem + 1, tem + 1 + n, sum[i] - t) - tem; 41 ans += (i - 1 - getsum(y - 1)); 42 add(x, 1ll); 43 } 44 printf("%lld\n", ans); 45 return 0; 46 }