返回顶部

Codeforces Round #510 (Div. 2) D. Petya and Array (权值线段树)

  • 题意:有一组数,问你有多少子区间的和\(<t\).

  • 题解:先用前缀和优化,可以表示成\(sum[r]-sum[l-1]<t\).移项得到:\(sum[l-1]>sum[r]-t\).那么就可以用一棵权值线段树来解决此问题,每次插入\(sum[r]\)的时候查询有多少满足条件的\(sum[l-1]\),这里不懂的可以去看看逆序对的求法。这题还有个问题,权值太大了,需要离散化,但是我懒(,用动态开点也行。

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 5e5 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    #define int long long
    
    int n;
    ll t;
    ll a[N];
    ll sum[N];
    ll tr[N<<4];
    ll ls[N<<4],rs[N<<4];
    ll base=1e15;
    ll rt,idx;
    
    void update(int &u,int l,int r,ll x){
        if(!u) u=++idx,ls[u]=rs[u]=0;
        if(l==r){
            tr[u]++;
            return;
        }
        ll mid=(l+r)>>1;
        if(x<=mid) update(ls[u],l,mid,x);
        else update(rs[u],mid+1,r,x);
        tr[u]=tr[ls[u]]+tr[rs[u]];
    }
    
    ll query(int u,int l,int r,int x,int y){
        if(l>=x && r<=y) return tr[u];
        ll res=0;
        ll mid=(l+r)>>1;
        if(x<=mid) res+=query(ls[u],l,mid,x,y);
        if(y>mid) res+=query(rs[u],mid+1,r,x,y);
        return res;
    }
    
    signed main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>n>>t;
        for(int i=1;i<=n;++i){
            cin>>a[i];
            sum[i]=sum[i-1]+a[i];
        }   
        update(rt,1,base<<1,0+base);
        ll ans=0;
        for(int i=1;i<=n;++i){
            ans+=query(rt,1,base<<1,sum[i]-t+base+1,base<<1);
            update(rt,1,base<<1,sum[i]+base);
        }
        cout<<ans<<'\n';
        return 0;
    }
    
posted @ 2021-08-29 17:29  Rayotaku  阅读(43)  评论(0编辑  收藏  举报