題解 康娜的線段樹

2021-10-14 16:44:11 字數 3606 閱讀 3152

小林是個程式媛,不可避免地康娜對這種人類的「魔法」產生了濃厚的興趣,於是小林開始教她oi。

今天康娜學習了一種叫做線段樹的神奇魔法,這種魔法可以維護一段區間的資訊,是非常厲害的東西。康娜試著寫了一棵維護區間和的線段樹。由於她不會打標記,因此所有的區間加操作她都是暴力修改的。具體的**如下:

struct segment_tree

inline

void

build

(int o,

int l,

int r)

int mid=

(l+r)

>>1;

build

(lson,l,mid)

;build

(rson,mid+

1,r)

;pushup

(o);

}inline

void

change

(int o,

int l,

int r,

int q,

int v)

int mid=

(l+r)

>>1;

if(q<=mid)

change

(lson,l,mid,q,v)

;else

change

(rson,mid+

1,r,q,v)

;pushup

(o);

}}t;

在修改時,她會這麼寫:

for

(int i=l;i<=r;i++

)t.change(1

,1,n,i,addv)

;

顯然,這棵線段樹每個節點有乙個值,為該節點管轄區間的區間和。

康娜是個愛思考的孩子,於是她突然想到了乙個問題:

1 ≤n

,m≤1

061 \leq n,m \leq 10^6

1≤n,m≤

106−

1000≤a

i,x≤

1000

-1000 \leq a_i,x \leq 1000

−1000≤

ai​,

x≤10

00對於本題,我們有乙個最樸素的想法就是對每一層求乙個和,乘上到達這一層的概率,即 12d

ep−1

\frac}

2dep−1

1​,但這樣是無法比較快速的應對修改操作的

基於這種思路,我們換個思考的方向,即思考乙個 iii

顯然包含 i

ii 的塊是連續的,如下圖:

對於包含 2

22 下標的塊,一定是連續的

而他所做的貢獻就是上圖標註的

所以現在我們只需要知道每乙個下標 i,i

∈[1,

n]i,i\in [1,n]

i,i∈[1

,n],它對應的葉節點的深度是多少

所以為了實現這一目標,我們來單獨去求這個東西:

求深度是可以用乙個搜尋來解決的,但是我們要快,乙個搜尋是不夠的

那麼優化搜尋的方法是什麼?

記憶化!

對的,我們在這裡有乙個比較好玩的性質,對於每乙個長度相同的區間,這兩個區間中相對應的兩個下標 i,j

i,ji,

j 到這兩個下標的最深深度距離是一樣的

可能有點抽象,我們舉個栗子:

上圖中第 2

22 層的 [1,

3][1,3]

[1,3

] 和 [4,

6][4,6]

[4,6

],區間長度一樣

那麼[ 1,

3][1,3]

[1,3

]區間中的第 2

22 個數也就是 2

22,它到[2,

2][2,2]

[2,2

]的距離是 222;

[ 4,

6][4,6]

[4,6

]區間中的第 2

22 個數也就是 5

55,它到[5,

5][5,5]

[5,5

]的距離也是 222;

當然,我們多找幾個來舉例,也是一樣的

也就是說我們是可以通過這種方法實現記憶化

void

dfs(

int l,

int r,

int sum)

return;}

if(l==r)

int mid=

(l+r)

>>1;

dfs(l,mid,sum+1)

;dfs

(mid+

1,r,sum+1)

; s1[r-l+1]

=l; s2[r-l+1]

=sum;

mem[r-l+1]

=true

;}

處理好了深度,剩下的事情就真的簡單了,乙個字首和,ove

rover

over

#include

#include

using

namespace std;

const

int maxn=

1000005

;int s1[maxn]

;int s2[maxn]

;long

long s[maxn]

;bool mem[maxn]

;long

long pre[maxn]

;int dep[maxn]

,pre_d[maxn]

;int w;

void

dfs(

int l,

int r,

int sum)

return;}

if(l==r)

int mid=

(l+r)

>>1;

dfs(l,mid,sum+1)

;dfs

(mid+

1,r,sum+1)

; s1[r-l+1]

=l; s2[r-l+1]

=sum;

mem[r-l+1]

=true;}

intmain()

for(

int i=

1;i<=n;i++

)// for(int i=1;i<=n;i++)

long

long ans=0;

for(

int i=

1;i<=n;i++

)for

(int i=

1;i<=m;i++

)return0;

}

洛谷 P3924 康娜的線段樹 解題報告

小林是個程式媛,不可避免地康娜對這種人類的 魔法 產生了濃厚的興趣,於是小林開始教她 oi 今天康娜學習了一種叫做線段樹的神奇魔法,這種魔法可以維護一段區間的資訊,是非常厲害的東西。康娜試著寫了一棵維護區間和的線段樹。由於她不會打標記,因此所有的區間加操作她都是暴力修改的。具體的 如下 struct...

線段樹 題解

nyoj 1068 題目鏈結 題目意思 典型的線段樹,插線問線.不過多了乙個,a 操作某乙個區間乙個數整體加上乙個數 s 操作查詢某乙個區間的總和,q 操作,查詢這個區間有多少個奇數.下面是 線段樹延遲更新,奇數的個數更新時注意 如果變化的是奇數,那麼 現在區間奇數個數 區間長度 原本區間的奇數個數...

題解 線段樹 關於時間

這是本蒟蒻的原創題 確實很簡單 本蒟蒻loj賬號diogenes 本題的難點在於每個操作會重複多次。核心思想是把第t秒的操作提前到第一秒,再減去多餘的部分。有一種構造方法 ad d i.j add i.j 表示區間 i j i,j 需要加的值。de l i.j del i.j 表示區間 i j i,...