對於線段樹的研究

2022-09-19 16:48:15 字數 3872 閱讀 1304

線段樹(segment tree)是一種特殊的二叉樹,每個節點維護乙個區間,可以用來處理區間更新、單點修改、單點查詢、區間查詢的資料結構。

建樹使用分治的思想,將乙個區間 \((l,r)\) 拆成兩份,分別為 \((l,mid)\) 與 \((mid+1,r)\),分別處理,直到遇到葉子結點(\(l=r\))為止,再上推資料(pushup)。完成建樹。

線段樹的每乙個節點不止儲存了區間資訊,還儲存了維護的值(maintain value)。在建樹的過程中更新每個節點的資料。

單點查詢使用二分的思想。

如果查詢的點的下標是\(k\),那麼先遍歷節點 \((1,n)\)(根節點)。如果\(k\)小於\(mid\)那麼往左半區間搜尋,否則往右半區間搜尋。

如果需要查詢空間被當前空間包含,那麼直接返回該空間的值。

先用單點查詢的思想找到葉子節點,更改他,然後逐級上推即可。

區間修改使用了懶標記(lazy tag)的思想。

如果直接遍歷線段樹進行暴力修改,那麼有很大的可能性會超時,因此我們可以使用懶標記標記這個節點,等到需要使用的時候再進行下推標記(pushdown)

遇到了標記,直接將儲存的標記向下推,更改維護值,取消標記即可。

線段樹的應用過於廣泛,我們在後面的應用再進行展示。

線段樹整體空間複雜度比較難算,但是一定不會超過\(o(4n)\)。所以開陣列的時候可以開n<<2的陣列。

另外,標記陣列也需要開四倍。

除此之外,線段樹一般不需要其他輔助空間。

例題:洛谷p3372 【模板】線段樹 1

題目傳送門

如題,已知乙個數列,你需要進行下面兩種操作:

涉及操作:區間修改、區間查詢、建樹(這個可以忽略)。

#include#define int long long

using namespace std;

int n,m;

#define ls (now<<1)

#define rs (now<<1|1)

#define mid (l+r>>1)

int tree[100001<<2],tag[100001<<2];

void pushup(int now)

void pushdown(int now,int l,int r)

}void build(int now,int l,int r)

build(ls,l,mid);

build(rs,mid+1,r);

pushup(now);

}void add(int now,int l,int r,int x,int y,int k)

pushdown(now,l,r);

if(mid>=x) add(ls,l,mid,x,y,k);

if(mid=x&&r<=y)

pushdown(now,l,r);

int ret=0;

if(mid>=x) ret+=ask(ls,l,mid,x,y);

if(mid>n>>m;

build(1,1,n);

while(m--)

else

}

void push_down(int o,int l,int r,int mid,int lson,int rson)

void addall(int o,int l,int r,int a,int b,int x)

else

}void mulall(int o,int l,int r,int a,int b,int x)

else

}long long query(int o,int l,int r,int a,int b)

}} tree;

signed main()

tree.build(1,1,n);

for(int i=1;i<=m;i++)

else if(f==2)

elseop[n];

struct number num[n];

bool cmp1(number a, number b)

bool cmp2(number a, number b)

void build(signed i, signed l, signed r)

tangmul[i] = 1;

signed mid = l + r >> 1;

build(i << 1, l, mid);

build(i << 1 | 1, mid + 1, r);

minv[i] = min(minv[i << 1], minv[i << 1 | 1]);

maxv[i] = max(maxv[i << 1], maxv[i << 1 | 1]);

}void pushadd(signed i, signed l, signed r, int x)

void pushmul(signed i, signed l, signed r, int x)

void pushad(signed i, signed l, signed r, int x)

void pushset(signed i, signed l, signed r, int x)

void pushdown(signed i, signed l, signed r)

if (tangmul[i] != 1)

if (tagadd[i] != 0)

if (tat[i] != 0)

}void setmin(signed i, signed l, signed r)

if (l == r)

return ;

pushdown(i, l, r);

signed mid = l + r >> 1;

setmin(i << 1, l, mid);

if (minv[i << 1 | 1] < mn)

setmin(i << 1 | 1, mid + 1, r);

minv[i] = min(minv[i << 1], minv[i << 1 | 1]);

maxv[i] = max(maxv[i << 1], maxv[i << 1 | 1]);

}void setmax(signed i, signed l, signed r)

if (l == r)

return ;

pushdown(i, l, r);

signed mid = l + r >> 1;

setmax(i << 1 | 1, mid + 1, r);

if (maxv[i << 1] > mx)

setmax(i << 1, l, mid);

minv[i] = min(minv[i << 1], minv[i << 1 | 1]);

maxv[i] = max(maxv[i << 1], maxv[i << 1 | 1]);

}void getanswer(signed i, signed l, signed r)

pushdown(i, l, r);

signed mid = l + r >> 1;

getanswer(i << 1, l, mid);

getanswer(i << 1 | 1, mid + 1, r);

}signed main()

soi(num + 1, num + q + 1, cmp1);

build(1, 1, q);

for (signed i = 1; i <= n; i++)

getanswer(1, 1, q);

soi(num + 1, num + q + 1, cmp2);

for (signed i = 1; i <= q; i++)

return 0;

}

對於CFileDialog的研究

cfiledialog 建構函式 cfiledialog bool bopenfiledialog,lpctstr lpszdefext null,lpctstr lpszfilename null,dword dwflags ofn hidereadonly ofn overwriteprompt...

線段樹2對於Pushdown的理解

最近才把這玩意兒搞出來,和解說如下 include includeusing namespace std const int n 100005 struct sd node n 2 int ini n root,cnt 0,q void buildtree int k,int l,int r els...

CQBZOJ 2856 線段樹 原子核研究

線段樹水題專項賽 portal 要求一種資料結構使得能夠支援插入乙個值,刪除乙個值,查詢最小的差值。所有輸入資料小於100000,並且你需要自動去重 一看,這不一思博線段樹嗎?維護每乙個區間左右有多少空格,中間 不包含左右端點 的最短區間,然後兩兩合併就行了啊。於是迅速碼完 信心滿滿的交了,然後就爆...