題解 P3372 模板 線段樹 1

2022-03-28 20:29:03 字數 2481 閱讀 7575

題目

剛剛學了樹狀陣列的區間加法和區間求和操作,就用來水掉這題了

本篇適合學會樹狀陣列的人群

前置芝士:

普通樹狀陣列

差分樹狀陣列

學過樹狀陣列的人都知道,我們對於乙個陣列,進行處理後,就可以在 \(o(\log n)\) 的時間內進行單點修改和區間求和

假設對於陣列 \(a_n\) ,我們用樹狀陣列的方法處理後,就可以單點修改任意 \(a_i\) 的值,或者求 \(\displaystyle \sum_^r a_n\)

如果我們差分一下 \(a_n\) ,設 \(c_n=a_n-a_\) ,那就可以使得樹狀陣列區間修改和單點查詢 \(\displaystyle a_n=\sum_^n c_i\)

在差分的基礎下,我們如果需要求區間和,則先相同地:

\(\displaystyle \sum_^ra_i=\sum_^ra_i-\sum_^a_i\)

所以,在此基礎上,我們只需要求解 \(\displaystyle \sum_^k a_i\) 即可求解區間求和問題了

我們變型這個式子:

\(\displaystyle \sum_^k a_i=\sum_^k\sum_^ic_i=\sum_^k(k+1-i)c_i\)

這玩意兒沒辦法直接求,所以我們把它分開來:

\(\displaystyle \sum_^k a_i=(k+1)\sum_^k c_i-\sum_^k ic_i\)

這樣,就能分開來,用樹狀陣列實現求和了。

因此,我們只需要實現 \(c_n\) 和 \(nc_n\) 的樹狀陣列,即可以實現區間求和

即前者: \(c_n\) 差分樹狀陣列的區間和,乘上 \((k+1)\) ,減去後者: \(nc_n\) 差分樹狀陣列的區間和

當然,我們還應考慮修改對吧

假設修改區間 \([l,r]\) 都加上 \(a\)

首先, \(c_n\) 的修改肯定是沒問題的:\(c_l+=a,c_+=-a\)

分解為差分樹狀陣列單點修改 \(c_l\) 和 \(c_\)

而我們很清楚,假設 \(c_i\) 轉變為 \((c_i+a)\)

則有 \(ic_i\) 轉變為 \(i(c_i+a)=ic_i+ia\)

也就是說,\(ic_i+=ia\) ,而 \(ic_i\) 的增加對本身累加了 \(ic_i\) 的貢獻都是增加 \(ia\) 的

所以只要在修改 \(c_i+=a\) 的時候,順便修改 \(ic_i+=ia\) 即可

那本蒟蒻就放 我碼風極醜的 **了:

#includeusing namespace std;

#define f(a,b,c,d) for(register int a=b,c=d;a<=c;a++)

#define g(a,b,c,d) for(register int a=b,c=d;a>=c;a--)

#define local

typedef int i32;

typedef unsigned int u32;

typedef long long int i64;

typedef unsigned long long int u64;

const i32 maxn=1e5+10;

typedef i64 ar[maxn];

//一堆條件反射的定義

namespace habit

#else

inline char gc() ,*p1=s,*p2=s;

return (p1==p2)&&(p2=(p1=s)+fread(s,1,1<<20,stdin),p1==p2)?eof:*(p1++);

}#endif

inline i32 read()

char output_ans[1<<20|1],*output_cur=output_ans;

inline void output()

inline void print(char c)

inline void print(char *s)

inline void print(u64 x)

char buf[30]=,*p=buf+28;

while(x) *(p--)=x%10+48,x/=10;

print(p+1);

}inline void print(i64 x)

}using namespace habit;

//正文開始

i32 d_n,d_m;

ar ar_d_ic,ar_d_c;

inline void add(i32 pos,i64 a)

inline i64 sum(i32 pos)

inline void update(i32 h,i32 t,i64 a)

inline i64 query(i64 h,i64 t)

int main()

else

output();

return 0;

}

最後安利以下 本蒟蒻的部落格

題解 P3372 模板 線段樹1

看了一下題解裡的zkw線段樹,感覺講的不是很清楚啊 可能有清楚的但是我沒翻到,望大佬勿怪 決定自己寫一篇。希望大家能看明白。zkw線段樹是一種優秀的非遞迴線段樹,速度比普通線段樹快兩道三倍,同時 量不大。當然,存在很多線段樹可做zkw不可做的題 zkw線段樹的核心思路就是先修改葉子,然後從底向上沿著...

P3372 模板 線段樹 1

線段樹學習 這個題來看,線段樹分為建樹,更新,查詢。1.建樹 void build ll p,ll l,ll r ll mid l r 1 build lson p l,mid build rson p mid 1,r push up sum p void push up sum ll p 這段 的...

P3372 模板 線段樹 1

題 include includeusing namespace std typedef long long ll ll n,m,ans,x,y,op,val 因為下面有的函式需要用到x,y,val值,懶得傳參,故直接寫為全域性變數 const int n 100000 struct nodetre...