【模板】树链剖分 P3384
生活随笔
收集整理的這篇文章主要介紹了
【模板】树链剖分 P3384
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接
//部分轉自:https://www.luogu.org/problemnew/solution/P3384
初學樹鏈剖分,感覺這個模板題還是容易理解的,但是實在是碼量很大的。
知識點:
- 重兒子:對于每一個非葉子節點,它的兒子中 兒子數量最多的那一個兒子 為該節點的重兒子
- 輕兒子:對于每一個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子
- 葉子節點沒有重兒子也沒有輕兒子(因為它沒有兒子。。)
- 重邊:連接任意兩個重兒子的邊叫做重邊
- 輕邊:剩下的即為輕邊
- 重鏈:相鄰重邊連起來的 連接一條重兒子 的鏈叫重鏈
- 對于葉子節點,若其為輕兒子,則有一條以自己為起點的長度為1的鏈
- 每一條重鏈以輕兒子為起點
- (太智能了,自己彈出來一張圖,
首先是通過兩個dfs來將信息進行處理
dfs1()
這個dfs要處理幾件事情:
- 標記每個點的深度dep[]
- 標記每個點的父親fa[]
- 標記每個非葉子節點的子樹大小(含它自己)
- 標記每個非葉子節點的重兒子編號son[]
void dfs1(int u,int pre,int dep)
{f[u]=pre;d[u]=dep;size[u]=1;for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v==pre) continue;dfs1(v,u,dep+1);size[u]+=size[v];if(size[v]>size[son[u]])son[u]=v;}
}
dfs2()
這個dfs2也要預處理幾件事情
- 標記每個點的新編號
- 賦值每個點的初始值到新編號上
- 處理每個點所在鏈的頂端
- 處理每條鏈
void dfs2(int u,int tp)
{top[u]=tp;dfn[u]=++t;tim[t]=u;if(son[u]) dfs2(son[u],tp);for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v!=f[u]&&v!=son[u]) dfs2(v,v);}
}
順序:先處理重兒子再處理輕
大概重要的過程就是這兩個了,通過輕重兒子劃分(先處理重兒子再處理輕)首先可以將其樹形結構轉化為線性結構,用于線段樹來處理各種的樹上信息,但是不同于dfs序直接遍歷,樹鏈剖分后的新編號,在大多數詢問處理的區間中都是連續區間,其實就是把樹拆成各個鏈,然后優雅(baoli)的求解各種問題。
代碼:
//#pragma comment (linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<stack>
#include<vector>
#include<map>
#include<queue>
#include<list>
#include<time.h>
#define myself i,l,r
#define lson i<<1
#define rson i<<1|1
#define Lson i<<1,l,mid
#define Rson i<<1|1,mid+1,r
#define half (l+r)/2
#define lowbit(x) x&(-x)
#define min4(a, b, c, d) min(min(a,b),min(c,d))
#define min3(x, y, z) min(min(x,y),z)
#define max3(x, y, z) max(max(x,y),z)
#define max4(a, b, c, d) max(max(a,b),max(c,d))
#define pii make_pair
#define pr pair<int,int>
//freopen("E://1.in","r",stdin);
//freopen("E://1.out","w",stdout);
typedef unsigned long long ull;
typedef long long ll;
const int inff = 0x3f3f3f3f;
const long long inFF = 9223372036854775807;
const int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
const int mdir[8][2] = {0, 1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 1, -1, -1, -1};
const double eps = 1e-10;
const double PI = acos(-1.0);
const double E = 2.718281828459;
using namespace std;
//const int mod=1e9+7;
const int maxn=2e5+5;
int d[maxn];//深度
int son[maxn];//某點的重兒子
int top[maxn];//該點所在鏈首
int dfn[maxn];//某件點的時間戳
int tim[maxn];//記錄某時間戳對應的節點下標
int a[maxn];//節點初始權值
int size[maxn];//以改點為根的子樹個數(包括自己
int f[maxn];//前驅
int head[maxn],sign,t;
int n,root,q,mod;
int tree[maxn<<2],lazy[maxn<<2];
struct node
{int to,p;
}edge[maxn<<1];
void init()
{for(int i=0;i<=n;i++) head[i]=-1;sign=t=0;
}
void add(int u,int v)
{edge[sign]=node{v,head[u]};head[u]=sign++;
}
void dfs1(int u,int pre,int dep)
{f[u]=pre;d[u]=dep;size[u]=1;for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v==pre) continue;dfs1(v,u,dep+1);size[u]+=size[v];if(size[v]>size[son[u]])son[u]=v;}
}
void dfs2(int u,int tp)
{top[u]=tp;dfn[u]=++t;tim[t]=u;if(son[u]) dfs2(son[u],tp);for(int i=head[u];~i;i=edge[i].p){int v=edge[i].to;if(v!=f[u]&&v!=son[u]) dfs2(v,v);}
}
void pushup(int i,int l,int r)
{tree[i]=(tree[lson]+tree[rson])%mod;
}
void pushdown(int i,int l,int r)
{if(lazy[i]){int mid=half;tree[lson]+=(mid-l+1)*lazy[i];tree[rson]+=(r-mid)*lazy[i];lazy[lson]+=lazy[i];lazy[rson]+=lazy[i];lazy[i]=0;}
}
void build(int i,int l,int r)
{if(l==r){tree[i]=a[tim[l]];return;}int mid=half;build(Lson);build(Rson);pushup(myself);
}
void update(int i,int l,int r,int ql,int qr,int val)
{if(ql<=l&&qr>=r){tree[i]+=val*(r-l+1);lazy[i]+=val;return;}int mid=half;pushdown(myself);if(qr<=mid) update(Lson,ql,qr,val);else if(ql>mid) update(Rson,ql,qr,val);else update(Lson,ql,mid,val),update(Rson,mid+1,qr,val);pushup(myself);
}
int query(int i,int l,int r,int ql,int qr)
{if(ql<=l&&qr>=r) return tree[i]%mod;pushdown(myself);int mid=half;if(qr<=mid) return query(Lson,ql,qr);else if(ql>mid) return query(Rson,ql,qr);else return query(Lson,ql,mid)+query(Rson,mid+1,qr);
}
void solve1(int x,int y,int val)//把x,y的簡單路徑給加上val,解釋如下
{while(top[x]!=top[y]){if(d[top[x]]<d[top[y]]) swap(x,y);update(1,1,n,dfn[top[x]],dfn[x],val);x=f[top[x]];}if(d[x]>d[y]) swap(x,y);update(1,1,n,dfn[x],dfn[y],val);
}
int solve2(int x,int y)
{int ans=0;while(top[x]!=top[y])//當兩個點不在同一條鏈上{if(d[top[x]]<d[top[y]]) swap(x,y);//把x定義為深度比較深的點,讓他往上邊跳ans=(ans+query(1,1,n,dfn[top[x]],dfn[x]))%mod;//ans加上x點到x所在鏈頂端 這一段區間的點權和x=f[top[x]];//把x跳到x所在鏈頂端的那個點的上面一個點}if(d[x]>d[y]) swap(x,y);//此時兩點就在一條鏈上了ans=(ans+query(1,1,n,dfn[x],dfn[y]))%mod;//一條鏈就直接區間和return ans;
}
void solve3(int x,int val)//把該點
{update(1,1,n,dfn[x],dfn[x]+size[x]-1,val);
}
int solve4(int x)
{return query(1,1,n,dfn[x],dfn[x]+size[x]-1)%mod;
}
int main()
{
// freopen("E://testdata.in","r",stdin);
// freopen("E://1.out","w",stdout);cin>>n>>q>>root>>mod;init();int x,y,op,val;for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=a[i]%mod;for(int i=1;i<n;i++)scanf("%d %d",&x,&y),add(x,y),add(y,x);dfs1(root,0,1);dfs2(root,root);build(1,1,n);while(q--){scanf("%d",&op);if(op==1) {scanf("%d %d %d",&x,&y,&val);solve1(x,y,val%mod);}else if(op==2) {scanf("%d %d",&x,&y);printf("%d\n",solve2(x,y));}else if(op==3) {scanf("%d %d",&x,&val);solve3(x,val%mod);}else {scanf("%d",&x);printf("%d\n",solve4(x));}}
}
?
總結
以上是生活随笔為你收集整理的【模板】树链剖分 P3384的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MUV LUV EXTRA 2019CC
- 下一篇: P1505 [国家集训队]旅游 树链剖分