Codeforces Round #510 (Div. 2) D. Petya and Array (树状数组)

版权声明:希望能在自己成长的道路上帮到更多的人,欢迎各位评论交流 https://blog.csdn.net/yiqzq/article/details/82926907

原题地址:http://codeforces.com/contest/1042/problem/D

题意:求所有 [ l , r ] [ l , r ] 区间和小于 T T 的这样的区间数量。

思路:从 l l r r 的和小于 T T ,即 s u m ( r ) s u m ( l 1 ) < T sum(r)-sum(l-1)<T ,其中 s u m sum a a 的前缀和。实现的方法就是从前往后对于每一个 s u m ( i ) sum(i) ,看在它前面有多少个大于 s u m ( i ) t sum(i)-t 的前缀和。

这个过程用树状数组来维护,每次加入 s u m ( i 1 ) sum(i-1) ,然后查询 s u m ( i ) sum(i) 。这样可以保证每次查询 s u m ( i ) sum(i) 的时候,存在于树状数组中的必然是在i之前的前缀和。

#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
int lowbit(int x) {
    return x & -x;
}
ll n, t;
ll bit[maxn];
ll a[maxn], b[maxn];
void add(ll i, ll x) {//第i个位置+x
    while (i <= (n + 1)) { //有n+1个前缀和数字
        bit[i] += x;
        i += lowbit(i);
    }
}
ll query(ll x) {
    ll ans = 0;
    while (x) {
        ans += bit[x];
        x -= lowbit(x);
    }
    return ans;
}

int main() {
    scanf("%lld%lld", &n, &t);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        a[i] += a[i - 1];
        b[i] = a[i];
    }
    sort(b, b + 1 + n);//这样子是为了存b[0]=0;
    ll cnt = 0;
    int pos;
    for (int i = 1; i <= n; i++) {
        pos = lower_bound(b, b + 1 + n, a[i - 1]) - b + 1;
        //不加1的话从0开始,树状数组的下标从1开始
        add(pos, 1);
//        pos = upper_bound(b, b + n + 1, a[i] - t ) - b ;//这个也行
        pos = lower_bound(b, b + n + 1, a[i] - t + 1) - b;
        /*
        因为是sum[i-1]>sum[j]-t,是大于号
        所以要么low_bound里面加1,要么upper_bound
        */
        cnt += i - query(pos);

    }
    printf("%lld\n", cnt);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/82926907
今日推荐