樹鏈剖分筆記(輕重鏈剖分)
阿新 • • 發佈:2020-09-11
我今天居然一次提交就A了!驚喜!
樹鏈剖分可以在樹上維護點的權值,可以進行一條鏈上的求和和修改操作,也可以將一個子樹進行求和和修改。
我們的具體做法是要將這個樹放到一個序列裡,然後使用線段樹來維護它。
我們定義重鏈和輕鏈,重鏈是指一個節點連向它最重的子節點的邊,我們可以發現我們將相連的重鏈看做一條重鏈。
我們可以將一顆樹拆分成輕鏈和重鏈,優先dfs重鏈,可以使得一條重鏈的dfs序是連續的,所以重鏈線上段樹上也是一段連續的區間。
我們來找兩個點的最近公共祖先,通過重鏈來向上跳,可以找到兩個點的LCA,由於跳的都是重鏈,可以線段樹區間維護一下。
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define mid (l+r>>1) #define B printf("!"); using namespace std; int read() { int a = 0,x = 1; char ch = getchar(); while(ch > '9' || ch < '0'){ if(ch == '-') x = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ a = a*10 + ch-'0'; ch = getchar(); } return a*x; } const int N=1e6+7; int n,m,r,p; int arr[N]; int head[N],go[N],nxt[N],cnt; void add(int u,int v) { go[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt; } int siz[N],son[N],dfn[N],dis[N],str[N],pos[N],fa[N]; void dfs1(int u) { siz[u] = 1; for(int e = head[u];e;e = nxt[e]){ int v = go[e]; if(v == fa[u]) continue; dis[v] = dis[u] + 1; fa[v] = u; dfs1(v); if(siz[v] > siz[son[u]]) son[u] = v; siz[u] += siz[v]; } } void dfs2(int u,int h) { str[u] = h; dfn[u] = ++cnt; pos[cnt] = u; if(son[u]) dfs2(son[u],h); for(int e = head[u];e;e = nxt[e]){ int v = go[e]; if(v == fa[u] || v == son[u]) continue; dfs2(v,v); } } int lazy[N],tre[N]; void build(int root,int l,int r) { if(l == r) {tre[root] = arr[pos[l]];return;} build(root<<1,l,mid);build(root<<1|1,mid+1,r); tre[root] = (tre[root<<1]+tre[root<<1|1]) % p; } void push_down(int root,int l,int r) { (tre[root<<1] += (mid-l+1)*lazy[root]%p)%=p,(tre[root<<1|1] += (r-mid)*lazy[root]%p)%=p; (lazy[root<<1] += lazy[root])%=p,(lazy[root<<1|1] += lazy[root])%=p; lazy[root] = 0; } void modify(int root,int l,int r,int ql,int qr,int x) { if(l >= ql && r <= qr) {(tre[root] += (r-l+1)*x%p)%=p,(lazy[root] += x)%=p;return;} if(l > qr || r < ql) return; push_down(root,l,r); modify(root<<1,l,mid,ql,qr,x);modify(root<<1|1,mid+1,r,ql,qr,x); tre[root] = (tre[root<<1] + tre[root<<1|1])%p; } int query(int root,int l,int r,int ql,int qr) { if(l >= ql && r <= qr) return tre[root]; if(l > qr || r < ql) return 0; push_down(root,l,r); return (query(root<<1,l,mid,ql,qr)+query(root<<1|1,mid+1,r,ql,qr))%p; } void LCA_updata(int u,int v,int x) { while(str[u] != str[v]){ // printf("%d %d\n",u,v); if(dis[str[u]] < dis[str[v]]) swap(u,v); modify(1,1,n,dfn[str[u]],dfn[u],x); u = fa[str[u]]; } if(dis[u] > dis[v]) swap(u,v); modify(1,1,n,dfn[u],dfn[v],x); } void LCA_query(int u,int v) { int ret = 0; while(str[u] != str[v]){ if(dis[str[u]] < dis[str[v]]) swap(u,v); (ret += query(1,1,n,dfn[str[u]],dfn[u])) %= p; u = fa[str[u]]; } if(dis[u] > dis[v]) swap(u,v); (ret += query(1,1,n,dfn[u],dfn[v])) %= p; printf("%d\n",ret); } void tre_updata(int u,int x) { modify(1,1,n,dfn[u],dfn[u]+siz[u]-1,x); } void tre_query(int u) { int ret = query(1,1,n,dfn[u],dfn[u]+siz[u]-1); printf("%d\n",ret); } int main() { // freopen("in.in","r",stdin); // freopen("out.out","w",stdout); n = read(),m = read(),r = read(),p = read(); for(int i = 1;i <= n;i ++) arr[i] = read(); for(int i = 1;i < n;i ++){ int u = read(),v = read(); add(u,v);add(v,u); } cnt = 0; dfs1(r);dfs2(r,r); build(1,1,n); for(int i = 1;i <= m;i ++){ int op = read(); if(op == 1){ int x = read(),y = read(),z = read(); LCA_updata(x,y,z); } else if(op == 2) { int x = read(),y = read(); LCA_query(x,y); } else if(op == 3) { int x = read(),z = read(); tre_updata(x,z); } else if(op == 4) { int x = read(); tre_query(x); } } return 0; }