codefoces 1042D. Petya and Array(查询比当数大的个数) Splay或树状数组+离散化

在这里插入图片描述

题意:问你有多少对 ∑ l r &lt; t \sum_{l}^{r} &lt; t lr<t
前缀和可以sum[r] - sum[l-1] < t,即sum[r] < sum[l-1] + t。枚举r,即到当前有多少个sum[l-1]+t大于sum[r]。
就变成了一个查询问题,因为这题不是强制在线的,所以可以利用树状数组+离散化解决。

代码如下:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAX = 200010;
int N;
ll t;
ll a[MAX],pos[MAX*4];
ll sum[MAX];
int cnt;
ll dat[MAX*4];
ll Sum(int i){
    ll res = 0;
    while(i > 0){
        res += dat[i];
        i -= (i&-i);
    }
    return res;
}
void add(int i){
    while(i <= cnt){
        dat[i] += 1;
        i += (i&-i);
    }
}
int gethash(ll x){
    int v = lower_bound(pos+1,pos+cnt+1,x)-pos;
    return v;
}
void Display(){
    printf("pos =================\n");
    for(int i=1;i<=cnt;++i){
        printf("%lld ",pos[i]);
    }
    printf("\n");
}
int main(void){
    scanf("%d%lld",&N,&t);
    for(int i=1;i<=N;++i){
        scanf("%lld",&a[i]);
        sum[i] = sum[i-1] + a[i];
        pos[++cnt] = sum[i];
        pos[++cnt] = sum[i] + t;
    }
    pos[++cnt] = t;
    sort(pos+1,pos+cnt+1);
    cnt = unique(pos+1,pos+cnt+1)-(pos+1);
   // Display();
    ll res = 0;
    add(gethash(t));
    for(int i=1;i<=N;++i){
        int v = gethash(sum[i]);
        int temp = (i-Sum(v));
        res += temp;
        add(gethash(sum[i]+t));
 //       printf("tmp = %d,res = %lld\n",temp,res);
    }
    printf("%lld\n",res);

    return 0;
}

还一种做法,就是直接利用Splay在线查询即可。
代码如下:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAX = 200010;
int ch[MAX*2][2],Size[MAX*2],f[MAX*2],cnt[MAX*2];
ll key[MAX*2];
int sz,root;
inline int get(int x){
    return ch[f[x]][1] == x;
}
inline void clean(int x){
    ch[x][0] = ch[x][1] = f[x] = cnt[x] = key[x] = Size[x] = 0;
}
inline void update(int x){
    if(x){
        Size[x] = cnt[x];
        if(ch[x][0])    Size[x] += Size[ch[x][0]];
        if(ch[x][1])    Size[x] += Size[ch[x][1]];
    }
}
inline void Rotate(int x){
    int y = f[x],z = f[y];
    int kind = get(x);
    ch[y][kind] = ch[x][!kind];
    f[ch[y][kind]] = y;
    f[y] = x;ch[x][!kind] = y;
    f[x] = z;
    if(z)   ch[z][ch[z][1] == y] = x;
    update(y);update(x);
}
void splay(int x){
    for(int fa;(fa = f[x]);Rotate(x)){
        if(f[fa])   Rotate((get(x) == get(fa))? fa:x);
    }
    root = x;
}
bool Insert(ll x){
    if(root == 0){
        sz++;
        Size[sz] = cnt[sz] = 1;
        ch[sz][0] = ch[sz][1] = f[sz] = 0;
        root = sz;key[sz] = x;
        return true;
    }
    int now = root,fa = 0;
    while(true){
        if(key[now] == x){
            cnt[now]++;
            update(fa);
            splay(now);
            return false;
        }
        fa = now;
        now = ch[now][x > key[now]];
        if(now == 0){
            sz++;
            Size[sz] = cnt[sz] = 1;
            ch[sz][0] = ch[sz][1] = 0;
            key[sz] = x;f[sz] = fa;
            ch[fa][x > key[fa]] = sz;
            splay(sz);
            return true;
        }

    }
}
int QueryNumRank(ll x){
    int ans = 0,now = root;
    while(true){
        if(x < key[now])
            now = ch[now][0];
        else{
            ans += ch[now][0]?Size[ch[now][0]]:0;
            if(key[now] == x){
                splay(now);
                return ans+1;
            }
            ans += cnt[now];
            now = ch[now][1];
        }
    }
}
int QueryPre(){
    int k = ch[root][0];
    while(ch[k][1]) k = ch[k][1];
    return k;
}
void del(ll x){
    QueryNumRank(x);//rotate x to root;
    if(cnt[root] > 1)   cnt[root]--;
    else if(!ch[root][0] && !ch[root][1]){
        clean(root);
        root = 0;
    }
    else if(!ch[root][0]){
        int tmp = root;
        root = ch[root][1];
        f[root] = 0;
        clean(tmp);
    }
    else if(!ch[root][1]){
        int tmp = root;
        root = ch[root][0];
        f[root] = 0;
        clean(tmp);
    }
    else{
        int t = QueryPre();
        int tmp = root;
        splay(t);
        ch[root][1] = ch[tmp][1];
        f[ch[tmp][1]] = root;
        clean(tmp);
        update(root);
    }
}
ll sum[MAX];
int main(void){
    int N;
    ll a,t;
    scanf("%d%lld",&N,&t);
    for(int i=1;i<=N;++i){
        scanf("%lld",&a);
        sum[i] = sum[i-1] + a;
    }
    ll res = 0;
    Insert(t);
    for(int i=1;i<=N;++i){
        Insert(sum[i]);
        res += Size[ch[root][1]];
        del(sum[i]);
        Insert(sum[i]+t);

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值