值域線段樹and動態開線段樹


值域線段樹每一個節點代表一個值,其他沒什么區別

動態開樹就是節省了沒有用到節點,其中重要一點的是不需要節點是連續的(即id值是任意的,只要可以找到即可)

 

例題

Bzoj 4627 回轉壽司


 

題意

給n個數問區間和在L<=sum【r】-sum【l-1】<=R區間有多少個

(N≤100000,|Ai|≤100000,0≤L, R≤10^9)


 

分析

由於區間和不單調,所以不能二分求取,分析可得推出公式   sum(r)-R<=sum(l-1)<=sum(r)-L  ([l,r]是一個滿足條件的區間)

直接計算當前前綴和之前滿足條件的前綴和個數,累加起來則為答案

故考慮利用線段樹,每次讓對應的前綴和加1,然后統計滿足條件的個數,但是這個數最大可以達到1e10,不可能開這么大的空間,

但N最大10w,和的種類也就10w,故考慮動態開線段樹,每次只開根到這個節點上路徑上經過的點,這樣最多開log(N)*N即可,

這樣的話每次必須保存當前節點的左右兒子的id才可以准確訪問左右兒子,其他和線段樹差不多

trick:注意開始要處理sum【0】,這樣才可以保證當l==r時滿足條件也能被統計上


 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e7 + 10;
ll tree[maxn], lson[maxn], rson[maxn];
ll tot;
ll a[100005];
ll sum[100005];
const ll inf = 1e10;
int n;
ll root=1;

int newnode()
{
    ++tot;
    tree[tot] = lson[tot] = rson[tot] = 0;
    return tot;
}

void update(ll &x, ll l, ll r, ll val)
{
    if(!x)
        x=newnode();
    tree[x]++;
    if(l == r)
        return;
    ll mid = (l+r)>>1;
    if(val<=mid ) update(lson[x], l, mid, val);
    else   update(rson[x], mid+1, r, val);
}

ll query(ll x, ll l, ll r, ll ql, ll qr)
{
    if(ql<=l && qr>=r)
        return tree[x];
    ll mid = (l + r)>> 1;
    ll ans = 0;
    if(ql<=mid && lson[x]) ans +=query(lson[x], l , mid, ql, qr);
    if(qr>mid && rson[x]) ans+=query(rson[x], mid+1, r, ql, qr);
    return ans;
}

int main()
{
    ll n, l ,r;
    scanf("%lld%lld%lld", &n, &l, &r);
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    ll ans=0;
    update(root, -inf, inf, 0);
    for(int i = 1; i<=n;i++)
    {
        ans+=query(root, -inf, inf, sum[i]-r, sum[i]-l);
        update(root,-inf,inf,sum[i]);
    }
    printf("%lld\n", ans);
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM