【模板】輕重鏈剖分
阿新 • • 發佈:2020-11-30
目錄
【模板】輕重鏈剖分
題目
講解
總的來說,就是一個不難理解,碼量爆炸的東西
推幾篇題解,講得不錯
https://www.luogu.com.cn/blog/zengqinyi/solution-p3384
https://www.cnblogs.com/ivanovcraft/p/9019090.html
前置知識
線段樹(必備),倍增LCA(可以幫助理解,不會應該也可以),鏈式前向星(存圖,不會有人不會吧)
概念
重兒子(子樹結點最多的兒子),重邊(某個點到它的重兒子連成的邊),重鏈(重邊連成的鏈),輕兒子(除重兒子外的其它兒子),輕邊,輕鏈
int n , m , rt , mod;//如題所述,為了習慣,p換位mod
int dat[nn];//輸入的初始值
int dep[nn] , fa[nn] , siz[nn] , hvyson[nn];
//dep[i]:i結點深度,fa[i]:父節點,siz[i]:以i為根的子樹大小,hvyson[i]:i結點的重兒子
int id[nn] , top[nn];
//id[i]:i結點的新標號,等下(第二輪dfs)會講,top[i]:i所在重鏈的頂端結點
第一輪dfs
我們需要處理出這些:
int dep[nn] , fa[nn] , siz[nn] , hvyson[nn];
void dfs1(int x , int fa_ , int deep) {//x:當前結點,fa_:x的父結點,deep:當前深度 fa[x] = fa_; hvyson[x] = 0; int maxsiz = 0; dep[x] = deep; for(int i = head[x] ; i ; i = ed[i].nxt) {//鏈式前向星 if(ed[i].to == fa_)continue; dfs1(ed[i].to , x , deep + 1); siz[x] += siz[ed[i].to]; if(maxsiz < siz[ed[i].to]) maxsiz = siz[ed[i].to], hvyson[x] = ed[i].to; } ++siz[x]; }
第二輪dfs
我們需要處理出這些:
int id[nn] , top[nn];
void dfs2(int x , int chaintop) {//x:同上,chaintop:x所在的重鏈的頂端結點 static int cnt = 0; id[x] = ++cnt;//按照dfs序賦值新編號,以便線段樹操作 change(root , id[x] , id[x] , dat[x]);//線段樹修改,上傳x原權值到新編號上 top[x] = chaintop; if(hvyson[x] == 0)return;//無兒子 dfs2(hvyson[x] , chaintop);//優先處理重兒子,下面講 for(int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if(to == fa[x] || to == hvyson[x])continue; dfs2(to , to); } }
核心-查詢&修改
相信很多人都產生了疑問:為什麼要給結點按照dfs序重新編號,並優先處理重兒子呢?
由於是dfs序,那麼以任意一個結點為根,整顆子樹的新編號都是連續的,這就是說,我們可以直接利用線段樹修改或查詢整顆子樹的權值,這就把3,4操作的時間複雜度降到了log級別
由於我們優先處理重兒子,所以同一條重鏈上所有結點的編號都是連續的,這也為線段樹操作提供了方便,以2操作為例(程式碼解釋)
inline int path_query(int x , int y) {//詢問x~y路徑上的點權和
int res = 0;
while(top[x] != top[y]) {//x和y不在同一條重鏈上
if(dep[top[x]] < dep[top[y]])swap_(x , y);//強行讓x所處的重鏈的頂端深度更大
res += query(root , id[top[x]] , id[x]);//答案累加上從x到top[x]的權值
res %= mod;
x = fa[top[x]];//x跳到所處重鏈頂端的父結點
}
if(dep[x] > dep[y])swap_(x , y);//此時x,y已經處於同一條重鏈上,強行讓y結點深度更大
res += query(root , id[x] , id[y]); //答案累加上x~y的點權
return res % mod;
}
修改操作同理:
inline int path_add(int x , int y , int z) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]])swap_(x , y);
change(root , id[top[x]] , id[x] , z);
x = fa[top[x]];
}
if(dep[x] > dep[y])swap_(x , y);
change(root , id[x] , id[y] , z);
}
後記
樹鏈剖分這個東西真的不難,就是很繁瑣,如果想學就真的要沉下心來好好寫程式碼,不要急
程式碼
#include <iostream>
#include <cstdio>
#define nn 100010
#define ll long long
using namespace std;
int read() {
int re = 0 , sig = 1;
char c;
do if((c = getchar()) == '-')sig = -1; while(c < '0' || c > '9');
while(c >= '0' && c <= '9')re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return re * sig;
}
int n , m , rt , mod;
int dat[nn];
int dep[nn] , fa[nn] , siz[nn] , hvyson[nn];
int id[nn] , top[nn];
//SegmentTree-begin======================
struct SegmentTree{
int ls , rs , l , r;
ll dat , tag;
}tr[nn * 4];
int root;
int build(int l , int r) {
static int top = 1;
int p = top++;
tr[p].l = l , tr[p].r = r , tr[p].dat = 0 , tr[p].tag = 0;
if(l == r) tr[p].ls = tr[p].rs = 0;
else {
int mid = (l + r) / 2;
tr[p].ls = build(l , mid);
tr[p].rs = build(mid + 1 , r);
}
return p;
}
inline void spread(int p) {
if(tr[p].tag != 0) {
int ls = tr[p].ls , rs = tr[p].rs;
tr[ls].dat += (tr[ls].r - tr[ls].l + 1) * tr[p].tag;
tr[rs].dat += (tr[rs].r - tr[rs].l + 1) * tr[p].tag;
tr[ls].dat %= mod; tr[rs].dat %= mod;
tr[ls].tag += tr[p].tag;
tr[rs].tag += tr[p].tag;
tr[ls].tag %= mod; tr[rs].dat %= mod;
tr[p].tag = 0;
}
return;
}
void change(int p , int l , int r , ll dat) {
if(l <= tr[p].l && r >= tr[p].r) {
tr[p].dat += (tr[p].r - tr[p].l + 1) * dat;
tr[p].tag += dat;
return;
}
spread(p);
if(l > tr[p].r || r < tr[p].l)return;
change(tr[p].ls , l , r , dat);
change(tr[p].rs , l , r , dat);
tr[p].dat = (tr[tr[p].ls].dat + tr[tr[p].rs].dat) % mod;
}
int query(int p , int l , int r) {
if(l <= tr[p].l && r >= tr[p].r)return tr[p].dat;
if(l > tr[p].r || r < tr[p].l) return 0;
spread(p);
return query(tr[p].ls , l , r) + query(tr[p].rs , l , r);
}
//SegmentTree-end========================
//=======================================
struct ednode{
int nxt , to;
}ed[nn * 2];
int head[nn];
inline void addedge(int u , int v) {
static int top = 1;
ed[top].to = v , ed[top].nxt = head[u] , head[u] = top;
++top;
}
//=======================================
void dfs1(int x , int fa_ , int deep) {
fa[x] = fa_;
hvyson[x] = 0;
int maxsiz = 0;
dep[x] = deep;
for(int i = head[x] ; i ; i = ed[i].nxt) {
if(ed[i].to == fa_)continue;
dfs1(ed[i].to , x , deep + 1);
siz[x] += siz[ed[i].to];
if(maxsiz < siz[ed[i].to])
maxsiz = siz[ed[i].to],
hvyson[x] = ed[i].to;
}
++siz[x];
}
void dfs2(int x , int linktop) {
// cout << x <<' ';
static int cnt = 0;
id[x] = ++cnt;
change(root , id[x] , id[x] , dat[x]);
top[x] = linktop;
if(hvyson[x] == 0)return;
dfs2(hvyson[x] , linktop);
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(to == fa[x] || to == hvyson[x])continue;
dfs2(to , to);
}
}
//========================================
inline void tree_add(int x , int z) {
change(root , id[x] , id[x] + siz[x] - 1 , z);
}
inline int tree_query(int x) {
return query(root , id[x] , id[x] + siz[x] - 1);
}
inline void swap_(int &a , int &b){int tmp=a;a=b;b=tmp;}
inline int path_add(int x , int y , int z) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]])swap_(x , y);
change(root , id[top[x]] , id[x] , z);
x = fa[top[x]];
}
if(dep[x] > dep[y])swap_(x , y);
change(root , id[x] , id[y] , z);
}
inline int path_query(int x , int y) {
int res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]])swap_(x , y);
res += query(root , id[top[x]] , id[x]);
res %= mod;
x = fa[top[x]];
}
if(dep[x] > dep[y])swap_(x , y);
res += query(root , id[x] , id[y]);
return res % mod;
}
int main() {
n = read() , m = read() , rt = read() , mod = read();
for(int i = 1 ; i <= n ; i++)
dat[i] = read();
for(int i = 1 ; i < n ; i++) {
int u = read() , v = read();
addedge(u , v);
addedge(v , u);
}
root = build(1 , n);
dfs1(rt , 0 , 0);
dfs2(rt , rt);
while(m--) {
int op = read();
int x , y , z;
switch(op) {
case 1 :
x = read() , y = read() , z = read();
path_add(x , y , z);
break;
case 2 :
x = read() , y = read();
printf("%d\n" , path_query(x , y) % mod);
break;
case 3 :
x = read() , z = read();
tree_add(x , z);
break;
case 4 :
x = read();
printf("%d\n" , tree_query(x) % mod);
break;
}
}
return 0;
}