[COGS2632] [HZOI 2016] 數列操作d

題目

一個長度爲n 的序列,一開始序列數的權值都是0 ,有m 次操作
支持兩種操作,
1 L R x ,給區間[L,R] 內位置爲pos 的數加上(posL)x
0 L R ,查詢區間[L,R] 內的權值和
最終答案對1e9+7 取模

題解

這是我偶然翻到學弟學妹們出的一道題,於是就做了做。
首先這道題肯定能用線段樹做,隨便打打標記。
但是這道題也可以用樹狀數組做,更快,但是思想應該比線段樹複雜些,就當是鍛鍊一下了。
下面是樹狀數組做法。

便於理解,將題目中的xk 表示。
樹狀數組求區間[L,R] 權值和的一般做法就是Sum(R)Sum(L1) 。於是,首要任務就是找到正確的單點插入方法,使得可以利用樹狀數組求出Sum(x)

首先考慮只有一次插入[L1,R1] k1 ,那麼Sum(x) 如何求得呢?注意到,每次插入都很有規律,相當於一條線段,所以可以容易求出這個插入區間的一個區間段上的區間和。

  • L1xR1 時,
    Sum(x)=i=1L110+i=L1x(iL1)k1=(xL1)(xL1+1)k1/2

    K=k/2
    Sum(x)=(xL1)(xL1+1)K1=x2(K1)+x(12L1)K1+(L21L1)K1

    注意到K1,(12L1)K1,(L21L1)K1 都與x 無關,對[L1,R1] 中每個Sum(x) 都不變,於是可以單點插入這三種值,記他們爲A1,B1,C1 ,分別插入到樹狀數組A,B,C 中,分別代表二次項係數、一次項係數和常數項,則Sum(x)=A1x2+B1x+C1
  • x>R1 時,這條線段不存在了,那麼在R1+1 處插入A1,B1,C1 ,但這條線段對Sum(x) 有貢獻,需要將他的貢獻K1(R1L1)(R1L1+1) 插入到常數項的R1+1 位置。

現在考慮多條線段的情形,容易發現可以線性疊加,求出2 的逆元,優化一下代碼,這道題就做完了。

代碼

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define  kN   300010LL
#define  mod  1000000007LL
#define  half 500000004LL
#define  lb   (p&(-p))

int n, A[kN], B[kN], C[kN];

inline void Delta(int p,int w,int c[kN]) {
    for (;p<=n;p+=lb) (c[p] += w) %= mod;
}

inline int Sum(int p,int c[kN]) {
    int ret = 0;
    for (;p;p-=lb) (ret += c[p]) %= mod;
    return ret;
}

inline int Sum(int p) {
    return (1LL*Sum(p,A)*p%mod*p%mod+1LL*Sum(p,B)*p%mod+1LL*Sum(p,C)%mod)%mod;
}

inline void Modify(int L,int R,ll x) {
    x = x*half%mod;
    Delta(L,x,A);
    Delta(L,x*(1LL-2*L)%mod,B);
    Delta(L,x*L%mod*(L-1)%mod,C);
    Delta(R+1,-x,A);
    Delta(R+1,-x*(1LL-2*L)%mod,B);
    Delta(R+1,(-x*L%mod*(L-1)%mod + x*(R-L)%mod*(R-L+1)%mod)%mod,C);
}

inline void Query(int L,int R) {
    printf("%lld\n", ((1LL*Sum(R)-Sum(L-1))%mod+mod)%mod);
}

int main() {
    freopen("segment.in","r",stdin);
    freopen("segment.out","w",stdout);
    int m, L, R, x;
    r(n), r(m);
    while (m --> 0) {
        r(L);
        if (L)
            r(L), r(R), r(x),
            Modify(L,R,x);
        else
            r(L), r(R),
            Query(L,R);
    }
    END: getchar(), getchar();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章