1. 程式人生 > >2018.01.04 bzoj5291: [Bjoi2018]鏈上二次求和(線段樹)

2018.01.04 bzoj5291: [Bjoi2018]鏈上二次求和(線段樹)

傳送門
線段樹基礎題。
題意:給出一個序列,要求支援區間加,查詢序列中所有滿足區間長度在 [ L , R ] [L,R] 之間的區間的權值之和(區間的權值即區間內所有數的和)。


想題 5

5 分鐘,寫題 20 20 分鐘,調題兩小時真 T M TM
好玩

我們令 s s 表示字首和, s 2 s^2 表示字首和的字首和。
首先讀完題發現要求的是:
i = l r j = i n ( s j s j i ) \sum_{i=l}^r\sum_{j=i}^{n}(s_j-s_{j-i})

= i = l r ( s n 2 s i 1 2 s l i 2 ) \sum_{i=l}^r(s^2_n-s^2_{i-1}-s^2_{l-i})
= ( r l + 1 ) s n 2 i = l 1 r 1 s i 2 i = n r n l s i 2 (r-l+1)s^2_n-\sum_{i=l-1}^{r-1}s^2_i-\sum_{i=n-r}^{n-l}s^2_i
推到這裡發現用線段樹維護動態的 s 2 s^2 陣列即可。
考慮區間修改 [ l , r , v ] [l,r,v] 對於 s 2 s^2 陣列的影響。

  1. i < l i<l ,無影響。
  2. l i r l\le i\le r ,那麼 s i 2 + = v 1 + v 2 + . . . + v ( i l + 1 ) = v ( i l + 1 ) ( i l + 2 ) 2 = v 2 i 2 + v ( 3 2 l ) 2 i + v ( 1 l ) ( r l ) 2 s^2_i+=v*1+v*2+...+v*(i-l+1)=\frac{v(i-l+1)(i-l+2)}2=\frac v2i^2+\frac{v(3-2l)}2i+\frac{v(1-l)(r-l)}2
  3. r < i r<i ,那麼 s i 2 + = v 1 + v 2 + . . . + v ( r l + 1 ) + ( i r ) v ( r l + 1 ) = v ( r l + 1 ) i + v ( r l + 1 ) ( l r + 2 ) 2 s^2_i+=v*1+v*2+...+v*(r-l+1)+(i-r)*v*(r-l+1)=v(r-l+1)i+\frac{v(r-l+1)(-l-r+2)}2

然後就可以分開更新了。
然而我碼完之後常數太大, T L E TLE 了2個小時,然後換了寫法才過。
程式碼:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
#define ri register int
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
    return ans;
}
typedef long long ll;
const ll mod=1e9+7,N=2e5+5,inv=5e8+4;
int n,m;
ll a[N],s1[N],s2[N];
struct Node{ll l1,l2,l3,sum,a,b,c;}T[N<<2];
inline void pushup(int p){T[p].sum=(T[lc].sum+T[rc].sum)%mod;}
inline void pushnow(int p,ll a,ll b,ll c){
	T[p].a+=a,T[p].b+=b,T[p].c+=c,(T[p].sum+=a*T[