日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

简单dfs序 + 树链剖分

發(fā)布時(shí)間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 简单dfs序 + 树链剖分 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

樹(shù)鏈剖分

DFS序

先來(lái)講一講DFS序是什么東西,直接上圖,方便理解。

估計(jì)巨巨們應(yīng)該知道了DFS序的兩個(gè)重要的東西,in,outin,outin,out數(shù)組。

  • ininin數(shù)組就是這個(gè)點(diǎn)進(jìn)入DFS的時(shí)間。
  • outoutout數(shù)組就是這個(gè)點(diǎn)退出DFS遞歸棧的時(shí)間。
  • 這個(gè)時(shí)間要注意,當(dāng)有點(diǎn)進(jìn)入的時(shí)候才加,沒(méi)有點(diǎn)進(jìn)入的時(shí)候不加也不減。

所以我們發(fā)現(xiàn)一個(gè)節(jié)點(diǎn)的子樹(shù)的遍歷是將,剛好是區(qū)間[in,out][in, out][in,out],所以這里我們就可以通過(guò)線段樹(shù)或者樹(shù)狀數(shù)組來(lái)維護(hù)其子樹(shù)的節(jié)點(diǎn)權(quán)值以及節(jié)點(diǎn)權(quán)值的查詢。

DFS序的簡(jiǎn)單問(wèn)題

  • 操作一,對(duì)某個(gè)節(jié)點(diǎn)增加x的權(quán)值。
  • 操作二、對(duì)某個(gè)節(jié)點(diǎn)以及他的子樹(shù)都增加x的權(quán)值。
  • 操作三、查詢某個(gè)節(jié)點(diǎn)的權(quán)值。
  • 操作四、查詢某個(gè)節(jié)點(diǎn)的子樹(shù)的節(jié)點(diǎn)權(quán)值和。

對(duì)于這個(gè)問(wèn)題,直接對(duì)應(yīng)的就是線段樹(shù)的單點(diǎn)、區(qū)間更新,以及單點(diǎn)、區(qū)間查詢,直接上一個(gè)線段樹(shù)就行。

DFS序簡(jiǎn)單應(yīng)用題目 Apple Tree

思路

簡(jiǎn)單的dfsdfsdfs序的應(yīng)用,然后用樹(shù)狀數(shù)組或者線段是維護(hù)區(qū)間就行,這里樹(shù)狀數(shù)組簡(jiǎn)單些,直接用樹(shù)狀數(shù)組了。

代碼

// #include <bits/stdc++.h> #include <iostream> #include <cstring> #include <cstdio> #include <algorithm>using namespace std;const int N = 1e5 + 10;int a[N], tree[N], n, m, tot; int head[N], to[N << 1], nex[N << 1], cnt; int in[N], out[N];char op[10];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++; }void update(int pos, int x) {while(pos <= n) {tree[pos] += x;pos += (-pos) & (pos);} }int query(int pos) {int ans = 0;while(pos) {// cout << pos << endl;ans += tree[pos];pos -= (-pos) & (pos);}return ans; }void dfs(int rt, int fa) {in[rt] = ++tot;for(int i = head[rt]; ~i; i = nex[i])if(to[i] != fa)dfs(to[i], rt);out[rt] = tot; }int main() {// freopen("in.txt", "r", stdin);while(scanf("%d", &n) != EOF) {memset(head, -1, sizeof head);memset(tree, 0, sizeof tree);tot = cnt = 0;int x, y;for(int i = 1; i < n; i++) {a[i] = 1;update(i, 1);scanf("%d %d", &x, &y);add(x, y);add(y, x);}dfs(1, 0);a[n] = 1;update(n, 1);scanf("%d", &m);for(int i = 0; i < m; i++) {scanf("%s %d", op, &x);if(op[0] == 'Q') printf("%d\n", query(out[x]) - query(in[x] - 1));else {if(a[x]) update(in[x], -1), a[x] = 0;else update(in[x], 1), a[x] = 1;}}}return 0; }

樹(shù)鏈剖分

我們先約定一些數(shù)組的意義。

fa記錄當(dāng)前Index節(jié)點(diǎn)的父節(jié)點(diǎn)編號(hào)
son記錄當(dāng)前Index節(jié)點(diǎn)的重兒子編號(hào)
sz記錄當(dāng)前節(jié)點(diǎn)的子樹(shù)節(jié)點(diǎn)個(gè)數(shù)
dep記錄當(dāng)前節(jié)點(diǎn)的深度
top記錄當(dāng)前節(jié)點(diǎn)所在重鏈的最頂端的節(jié)點(diǎn)編號(hào)
rk記錄dfs序?yàn)镮ndex的節(jié)點(diǎn)編號(hào)
id記錄當(dāng)前Index節(jié)點(diǎn)的dfs序編號(hào)

第一次DFS

void dfs1(int rt, int f) {fa[rt] = f, sz[rt] = 1;//記錄當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn),子樹(shù)大小。dep[rt] = dep[f] + 1;//記錄當(dāng)前節(jié)點(diǎn)的深度。for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == f) continue;dfs1(to[i], rt);sz[rt] += sz[to[i]];//更新子樹(shù)大小。if(!son[rt] || sz[to[i]] > sz[son[rt]])son[rt] = to[i];//如果當(dāng)前節(jié)點(diǎn)沒(méi)有重兒子,或者出現(xiàn)了一個(gè)節(jié)點(diǎn)的子樹(shù)節(jié)點(diǎn)數(shù)量大于當(dāng)前的重兒子的子樹(shù)節(jié)點(diǎn)數(shù)量,則更新重兒子。} }

上一幅圖來(lái)理解dfs1更新的結(jié)果。

都是常規(guī)的更新,應(yīng)該看圖就懂了,唯一可能需要稍微一點(diǎn)點(diǎn)理解的就是son了。

第二次DFS

void dfs2(int rt, int t) {id[rt] = ++tot;rk[tot] = rt;top[rt] = t;//這個(gè)更新記錄的就是當(dāng)前節(jié)點(diǎn)所在重鏈的深度最小的節(jié)點(diǎn)。if(!son[rt]) return ;//如果沒(méi)有重兒子,說(shuō)明是葉節(jié)點(diǎn),遞歸邊界記得返回。dfs2(son[rt], t);//優(yōu)先更新當(dāng)前所在的重邊,for(int i = head[rt]; ~i; i = nex[i]) {//更新其字節(jié)點(diǎn)的重邊。if(to[i] == fa[rt] || to[i] == son[rt]) continue;dfs2(to[i], to[i]);//與重鏈間接相連的點(diǎn)一定是一條重鏈的top節(jié)點(diǎn)} }

上一幅圖來(lái)理解dfs2更新的結(jié)果,紅色的圈起來(lái)的是每一條重鏈。

這張圖是樹(shù)鏈剖分的精髓,把所有的邊劃分成了,重邊和輕邊,并且所有的節(jié)點(diǎn)依舊持有DFS序的優(yōu)秀性質(zhì),并且一條重鏈上的dfs序一定是連續(xù)的,因此我們同樣可以用線段樹(shù)等數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)和查詢區(qū)間。

這里還有一個(gè)問(wèn)題就是dfs2中為什么要先進(jìn)行,對(duì)其重兒子的dfs然后再對(duì)其它的兒子進(jìn)行dfs。

我們要保證top點(diǎn)的延續(xù)性,所以我們選擇先進(jìn)行重兒子的dfs。


接下來(lái)我們考慮兩種操作如何實(shí)現(xiàn)

一、將樹(shù)從x到y(tǒng)結(jié)點(diǎn)最短路徑上所有節(jié)點(diǎn)的權(quán)值和

我們的第一想法就是能不能把這兩個(gè)點(diǎn),通過(guò)轉(zhuǎn)換,變成最后在同一條重鏈上。

在我們上面記錄的變量中,有fa當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)編號(hào),所以我們能做的大概好像只有把這個(gè)節(jié)點(diǎn)向上移動(dòng),并且我們每次移動(dòng)可以跨越一整條重鏈,到與之相鄰的另一條重鏈,同時(shí)我們還可以通過(guò)區(qū)間和的查詢統(tǒng)計(jì)每次移動(dòng)的花費(fèi)。

從這個(gè)方向出發(fā),我們考慮,什么時(shí)候哪個(gè)節(jié)點(diǎn)可以進(jìn)行這樣的操作。

每一次移動(dòng)的dep[top]都會(huì)變小,如果我們讓dep[top]更小的去進(jìn)行這個(gè)操作,最后肯定是不可能到達(dá),top = top的,所以我們只有一個(gè)選擇,移動(dòng)dep[top]小的節(jié)點(diǎn),只有這樣,這兩個(gè)節(jié)點(diǎn)的top才有可能會(huì)相同,


舉個(gè)例子,統(tǒng)計(jì)4 -> 6的最短路的節(jié)點(diǎn)編號(hào)和

顯然dep[top[4]]=3<dep[top[6]]=1dep[top[4]] = 3 < dep[top[6]] = 1dep[top[4]]=3<dep[top[6]]=1,所以我們移動(dòng),節(jié)點(diǎn)4,統(tǒng)計(jì)sum(id[top[4]],id[4])=4sum(id[top[4]], id[4]) = 4sum(id[top[4]],id[4])=4,此時(shí)4到了2這個(gè)位置,我們發(fā)現(xiàn)top[2]=top[6]top[2] = top[6]top[2]=top[6],直接利用重鏈的dfs連續(xù)性,查詢sum(id[2],id[6])=13sum(id[2], id[6]) = 13sum(id[2],id[6])=13,最后得到sumans=4+13=17sum_{ans} = 4 + 13 = 17sumans?=4+13=17


二、將樹(shù)從x到y(tǒng)結(jié)點(diǎn)最短路徑上所有節(jié)點(diǎn)的權(quán)值加上Z

明白了上面的操作這個(gè)就簡(jiǎn)單了,無(wú)非是把區(qū)間查詢改成區(qū)間更新就行了

樹(shù)鏈剖分模板題

#include <bits/stdc++.h> #define mid (l + r >> 1) #define lson rt << 1, l, mid #define rson rt << 1 | 1, mid + 1, r #define ls rt << 1 #define rs rt << 1 | 1using namespace std;typedef long long ll; const int N = 2e5 + 10;ll sum[N << 2], lazy[N << 2]; int head[N], value[N], nex[N << 1], to[N << 1], cnt; int fa[N], sz[N], dep[N], id[N], rk[N], son[N], top[N], tot; int n, m, mod;void dfs1(int rt, int f) {dep[rt] = dep[f] + 1;sz[rt] = 1; fa[rt] = f;for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == f) continue;dfs1(to[i], rt);sz[rt] += sz[to[i]];if(!son[rt] || sz[to[i]] > sz[son[rt]])son[rt] = to[i];} }void dfs2(int rt, int t) {top[rt] = t;id[rt] = ++tot;rk[tot] = rt;if(!son[rt])return ;dfs2(son[rt], t);for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == fa[rt] || to[i] == son[rt]) continue;dfs2(to[i], to[i]);} }void updown(int rt, int l, int r) {if(lazy[rt]) {sum[ls] = (sum[ls] + (mid - l + 1) * lazy[rt] % mod) % mod;sum[rs] = (sum[rs] + (r - mid) * lazy[rt] % mod) % mod;lazy[ls] = (lazy[ls] + lazy[rt]) % mod;lazy[rs] = (lazy[rs] + lazy[rt]) % mod;lazy[rt] = 0;} }void build(int rt, int l, int r) {if(l == r) {sum[rt] = value[rk[l]];return ;}build(lson);build(rson);sum[rt] = (sum[ls] + sum[rs]) % mod; }ll query(int rt, int l, int r, int L, int R) {if(l >= L && r <= R) return sum[rt];updown(rt, l, r);ll ans = 0;if(L <= mid) ans += query(lson, L, R);if(R > mid) ans += query(rson, L, R);return ans; }void update(int rt, int l, int r, int L, int R, int k) {if(l >= L && r <= R) {sum[rt] = (sum[rt] + (r - l + 1) * k % mod) % mod;lazy[rt] = (lazy[rt] + k) % mod;return ;}updown(rt, l, r);if(L <= mid) update(lson, L, R, k);if(R > mid) update(rson, L, R, k);sum[rt] = (sum[ls] + sum[rs]) % mod; }void print(int rt, int l, int r) {if(l == r) {printf("%lld\n", sum[rt]);return ;}updown(rt, l, r);print(lson);print(rson); }void op1(int x, int y, int k) {while(top[x] != top[y]) {if(dep[top[x]] < dep[top[y]]) swap(x, y);update(1, 1, n, id[top[x]], id[x], k);x = fa[top[x]];}if(dep[x] > dep[y]) swap(x, y);update(1, 1, n, id[x], id[y], k); }ll op2(int x, int y) {ll ans = 0;while(top[x] != top[y]) {if(dep[top[x]] < dep[top[y]]) swap(x, y);ans = (ans + query(1, 1, n, id[top[x]], id[x])) % mod;x = fa[top[x]];}if(dep[x] > dep[y]) swap(x, y);ans = (ans + query(1, 1, n, id[x], id[y])) % mod;return ans % mod; }void op3(int x, int k) {update(1, 1, n, id[x], id[x] + sz[x] - 1, k); }ll op4(int x) {return query(1, 1, n, id[x], id[x] + sz[x] - 1); }void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++; }int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);memset(head, -1, sizeof head), cnt = 0;int x, y, rt, op, z;scanf("%d %d %d %d", &n, &m, &rt, &mod);for(int i = 1; i <= n; i++)scanf("%d", &value[i]);for(int i = 1; i < n; i++) {scanf("%d %d", &x, &y);add(x, y);add(y, x);}dfs1(rt, 0);dfs2(rt, rt);// int fa[N], son[N], sz[N], id[N], rk[N], tp[N], dep[N], tot;// for(int i = 1; i <= n; i++)// printf("%d %d %d %d %d %d\n", fa[i], son[i], sz[i], id[i], top[i], dep[i]);build(1, 1, n);// print(1, 1, n);// puts("");for(int i = 0; i < m; i++) {scanf("%d", &op);if(op == 1) {scanf("%d %d %d", &x, &y, &z);op1(x, y, z);}else if(op == 2) {scanf("%d %d", &x, &y);printf("%lld\n", op2(x, y) % mod);}else if(op == 3) {scanf("%d %d", &x, &y);op3(x, y);}else {scanf("%d", &x);printf("%lld\n", op4(x) % mod);}}// print(1, 1, n);return 0; }

[JLOI2014]松鼠的新家

其實(shí)是個(gè)模板附贈(zèng)題,比洛谷模板還簡(jiǎn)單,就一個(gè)操作,區(qū)間中每個(gè)節(jié)點(diǎn) + 1,然后要注意在最后的答案中,除了第一個(gè)被訪問(wèn)的點(diǎn),其他的ans都要減1處理,然后就完事了。

#include <bits/stdc++.h> #define mid (l + r >> 1) #define lson rt << 1, l, mid #define rson rt << 1 | 1, mid + 1, r #define ls rt << 1 #define rs rt << 1 | 1using namespace std;typedef long long ll; const int N = 3e5 + 10;ll sum[N << 2], lazy[N << 2], ans[N]; int head[N], to[N << 1], nex[N << 1], cnt; int fa[N], son[N], top[N], sz[N], dep[N], rk[N], id[N], tot; int n, a[N];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++; }void dfs1(int rt, int f) {fa[rt] = f, sz[rt] = 1;dep[rt] = dep[f] + 1;for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == f) continue;dfs1(to[i], rt);sz[rt] += sz[to[i]];if(!son[rt] || sz[to[i]] > sz[son[rt]])son[rt] = to[i];} }void dfs2(int rt, int t) {top[rt] = t;id[rt] = ++tot;rk[tot] = rt;if(!son[rt]) return ;dfs2(son[rt], t);for(int i = head[rt]; ~i; i = nex[i]) {if(to[i] == fa[rt] || to[i] == son[rt]) continue;dfs2(to[i], to[i]);} }void push_down(int rt, int l, int r) {if(lazy[rt]) {lazy[ls] += lazy[rt];lazy[rs] += lazy[rt];sum[ls] += lazy[rt] * (mid - l + 1);sum[rs] += lazy[rt] * (r - mid);lazy[rt] = 0;} }void update(int rt, int l, int r, int L, int R) {if(l >= L && r <= R) {lazy[rt] += 1;sum[rt] += r - l + 1;return ;}push_down(rt, l, r);if(L <= mid) update(lson, L, R);if(R > mid) update(rson, L, R);sum[rt] = sum[ls] + sum[rs]; }void query(int rt, int l, int r) {if(l == r) {ans[rk[l]] = sum[rt];return ;}push_down(rt, l, r);query(lson);query(rson); }void update_tree(int x, int y) {while(top[x] != top[y]) {if(dep[top[x]] < dep[top[y]]) swap(x, y);update(1, 1, n, id[top[x]], id[x]);x = fa[top[x]];}if(dep[x] > dep[y]) swap(x, y);update(1, 1, n, id[x], id[y]); }int main() {// freopen("in.txt", "r", stdin);memset(head, -1, sizeof head);scanf("%d", &n);for(int i = 1; i <= n; i++)scanf("%d", &a[i]);int x, y;for(int i = 1; i < n; i++) {scanf("%d %d", &x, &y);add(x, y);add(y, x);}dfs1(1, 0);dfs2(1, 0);for(int i = 1; i < n; i++)update_tree(a[i], a[i + 1]);query(1, 1, n);for(int i = 2; i <= n; i++)ans[a[i]]--;for(int i = 1; i <= n; i++)printf("%lld\n", ans[i]);return 0; }

總結(jié)

以上是生活随笔為你收集整理的简单dfs序 + 树链剖分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。