1, Tree chain subdivision
Idea: transform the problem on the tree into line segment, maintain it on the tree, and turn it into sequence on the tree
1. Heavy chain subdivision
Significance: heavy chain subdivision can divide any path on the tree into no more than O ( l o g n ) O(log n) O(logn) continuous chains, the depth of points on each chain is different from each other (that is, a chain from bottom to top, and the lca of all points on the chain is one end point of the chain) (a new method to find lca!!)
-
The heavy child node represents the largest child node in the subtree of its child node. If there are multiple sub trees with the largest sub node, take one of them; If there are no child nodes, there are no multiple child nodes.
-
Define light child nodes to represent all remaining child nodes.
-
The edge from this node to the heavy child node is the heavy edge.
-
Edges to other light child nodes are light edges.
-
Several heavy edges connected end to end form a heavy chain.
-
If the single node is also regarded as a heavy chain, the whole tree will be divided into several heavy chains.
Maintenance object: advanced data structures such as line segment tree or tree array
Subdivision process
int son[M],fa[M],size[M],dep[M]; int top[M],dfn[M]; int dfs_tree(int x,int f,int depth)(Preprocess some data on the tree (depth, maximum sub section, etc.) { siz[x]=1;fa[x]=f;dep[x]=depth; int smax=0; for(int i=h[x];~i;i=nxt[i]) { int v=to[i]; if(v!=f) { int ss=dfs_tree(v,x,depth+1); size[x]+=ss; if(ss>smax)son[x]=v; } } } void dfs_line(int x,int topx) { dfn[x]=++cnt; top[x]=topx; for(int i=h[x];~i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) { if(son[x]!=v)dfs_line(v,topx); else dfs_line(v,v); } } }
Indexing process
- Here, the midfy and query of the line segment tree are not written
- Pay attention to the operation of continuous chain jumping (intuitively recursive, actually circular)
void modify_path(int u,int v,ll d) { while (top[u]!=top[v]) { if(dep[top[u]]<dep[top[v] ] )swap(u,v); modify(1,id[top[u]],id[u],d); u=f[top[u]]; } if(dep[u]<dep[v])swap(u,v); modify(1,id[v],id[u],d); } ll query_path(int u,int v) { ll res= 0; while (top[u]!=top[v]) { if(dep[top[u]]<dep[top[v] ] )swap(u,v); res+= query(1,id[top[u] ],id[u]); u=f[top[u]]; } if(dep[u]<dep[v])swap(u,v); res= (res+query(1,id[v],id[u]) )%mod; return res; } void modify_tree(int x, ll d) { modify(1,id[x], id[x]+ sz[x]-1,d); } ll query_tree(int x) { return query(1,id[x],id[x]+sz[x]-1); }
Board: (small fresh writing method)
#include <iostream> #include <cstring> #include <algorithm> typedef long long ll; using namespace std; const int N= 2e5+9; struct node { int l,r; ll sum,add; #define sum(p) tr[p].sum #define add(p) tr[p].add #define l(p) tr[p].l #define r(p) tr[p].r }tr[N*4]; int h[N], e[N*2], nxt[N*2], idx; int cnt,n,m; ll wn[N],a[N],mod; int top[N],dep[N],sz[N]; int son[N],id[N],f[N]; void addx(int a, int b) { e[idx] = b, nxt[idx] = h[a], h[a] = idx ++ ; } template<typename T>inline void read(T &x) { x=0; bool f=1; char ch = getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch = getchar();} while(ch>='0'&&ch <='9'){x=(x<<1)+(x<<3)+ch-'0'; ch = getchar();} if(!f)x*=-1; } void wri(ll x) { if(x<0)putchar('-'),x=-x; if(x>9)wri(x/10); putchar (x%10+'0'); } void pushup(int p) { sum(p)= sum(p<<1)+ sum(p<<1|1); } void pushdown(int p) { sum(p<<1)+= (r(p<<1)- l(p<<1) +1)*add(p)%mod; sum(p<<1|1)+= (r(p<<1|1)- l(p<<1|1) +1)*add(p)%mod; add(p<<1)+= add(p); add(p<<1|1)+= add(p); add(p)= 0; } void build(int p,int l, int r) { tr[p]={l,r}; if(l==r) { tr[p] = {l,r,wn[l], 0}; return; } int mid = l+r >>1; build(p<<1,l,mid); build (p<<1|1,mid+1,r); pushup(p); } void modify(int p,int l,int r,ll d) { int lx= l(p); int rr= r(p); if(lx>=l&&rr<=r) { sum(p)+= (r(p)- l(p)+1)*d%mod; add(p)+= d; return; } pushdown(p); int mid= (lx+rr)>>1; if(l<=mid)modify(p<<1,l,r,d); if(r>mid)modify(p<<1|1,l,r,d); pushup(p); } ll query(int p,int l,int r) { int lx= l(p); int rr= r(p); if(lx>=l&&rr<=r)return sum(p); pushdown(p); ll ans= 0; int mid= (lx+rr)>>1; if(l<=mid)ans+= query(p<<1,l,r); if(r>mid)ans+= query(p<<1|1,l,r); return ans; } void dfs1(int p,int depth,int fa) { sz[p]=1; dep[p] =depth; f[p]=fa; for(int i=h[p];~i;i=nxt[i]) { int j= e[i]; if(j== fa)continue; dfs1(j,depth+1,p); if(sz[j]>sz[son[p]])son[p]=j; sz[p]+= sz[j]; } } void dfs2(int p,int t,int fa) { id[p]=++cnt; wn[cnt]=a[p] ; top[p]=t; if(!son[p])return; dfs2(son[p],t,p); for(int i=h[p];~i;i=nxt[i]) { int j= e[i]; if(j==son[p] || j==fa)continue; dfs2(j,j,p); } } void modify_path(int u,int v,ll d) { while (top[u]!=top[v]) { if(dep[top[u]]<dep[top[v] ] )swap(u,v); modify(1,id[top[u]],id[u],d); u=f[top[u]]; } if(dep[u]<dep[v])swap(u,v); modify(1,id[v],id[u],d); } ll query_path(int u,int v) { ll res= 0; while (top[u]!=top[v]) { if(dep[top[u]]<dep[top[v] ] )swap(u,v); res+= query(1,id[top[u] ],id[u]); u=f[top[u]]; } if(dep[u]<dep[v])swap(u,v); res= (res+query(1,id[v],id[u]) )%mod; return res; } void modify_tree(int x, ll d) { modify(1,id[x], id[x]+ sz[x]-1,d); } ll query_tree(int x) { return query(1,id[x],id[x]+sz[x]-1); } int main() { memset(h,-1,sizeof h); ll z; int root=1 ; read(n); read(m); read(root); read(mod); int x,y,op; for (int i = 1; i <= n; i ++ )read(a[i]); for(int i=2;i<=n;i++)read(x), read(y), addx(x,y), addx(y,x); dfs1(root,1,-1); dfs2(root,root,-1); build(1,1,n); for(int i= 1 ;i<=m;i++) { read(op); if(op==1) read(x), read(y) , read(z), modify_path(x,y,z); else if(op==2) read(x), read(y) , wri(query_path(x,y)%mod), puts(""); else if(op==3) read(x), read(z), modify_tree(x,z); else read(x), wri(query_tree(x)%mod), puts(""); } return 0; }
Tree section application
1. Edge weight transfer point
https://www.luogu.com.cn/blog/user17952/solution-p3038
For each edge, we weight the endpoint with greater depth, then the weighting (or query) of interval [l, r] is to remove the point with the smallest depth and weight (or query) the other points.
It sounds like a lot of trouble to deal with, but we can think about the process of tree section query:
-
1) During the last tree section query, the current two points x and y must be on the same heavy chain (the depth of Y is less than x)
-
2) According to the dfs order before processing, we first traverse the heavy son. Then the number of the heavy son in the line segment tree must be close to its parent node (i.e. its parent node number + 1), so we should ignore the point y with the smallest depth, That is, the processing interval [the number of Y in the line segment tree + 1, the number of X in the line segment tree] (in addition, it should be noted that when y = x, there will be an error in the execution in the line segment tree, which we need to judge specifically)
3. Precautions:
1. If there is no son in dfs2, return directly
2. Note that the edge weight is not lca, and the left end point is + 1
3. Pay attention to space
4. Don't forget the pushdown position
5. Note that the newly added point weight needs to be transformed into the sequence during initialization. Pay attention to dfs2, build and modify