树链剖分(从入门到入土。)

前置知识:线段树,链式前向星,LCA,DFS序

树链剖分通常的操作:
1.x -> y 的路径上修改 2.x -> y 的路径上查询 3. 对于 x 的子树修改 4.对于 x 的子树查询。

一般还有换根操作。树剖也也可以做LCA。

树链剖分有两个DFS 这两个DFS就是把一棵树变成一个序列。 然后就可以用数据结构来维护了。

第一个DFS 用来求 \(fa\)(祖先节点) \(size\)(子树大小)\(son\)(重儿子) \(d\)(深度)
重儿子指的是\(size\)较大的儿子节点。
第二个DFS 用来求\(top\)(这条链上最顶端的点) \(id\)(编号)以及其他的赋值操作。 但是重儿子要先DFS。

const int N = 1e5 + 10 ;
struct node { int v , nxt ; } ;
node e[N << 1] ;
int head[N] , cnt = 0 ;
inline void Add(int u , int v) { e[++ cnt].v = v ; e[cnt].nxt = head[u] ; head[u] = cnt ; }
int size[N] , son[N] , d[N] ;
inline void Dfs1(int u) { size[u] = 1 ;
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ; if(v == fa[u]) continue ; // 防止无限递归。
    d[v] = d[u] + 1 , fa[v] = u ,  // 记录深度 以及父亲节点
Dfs1(v) ; size[u] += size[v] ; //算出子树大小
    if(size[v] > size[son[u]]) son[u] = v ; //得出u 的 重儿子是 son[u]
  }
}
int top[N] , id[N] , tot = 0 ;
inline void Dfs2(int u , int tp) { top[u] = tp , id[u] = ++ tot ;
  if(! son[u]) return ; Dfs2(son[u] , tp) ;
  for(register int i = head[u] ; i ; i = e[i].nxt) { int v = e[i].v ;
    if(v ^ fa[u] && v ^ son[u]) Dfs2(v , v) ;
  }
}

这样就算是一个大概的板子了 没有赋值…(赋值根据编号搞一搞就可以了 下面会讲。)

【模板】树链剖分

#include <bits/stdc++.h>
using namespace std ;
inline int read() { register int res = 0 ; register char c ;
#define gc c = getchar()
    while(isspace(gc)) ;
    while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(gc)) ;
    return res ;
}
int n , m , r , p ;
const int N = 1000000 + 5 ;
struct E{ int v ;int nxt ; } ;
E edge[N << 1] ;
int a[N] ; int fa[N] ; int w[N] ; int id[N] ; int son[N] ;
int cnt = 0 ; int head[N] ; int Add[N << 2] , laz[N << 2] ;
int dep[N] , siz[N] , t[N] ;
inline void Add_Edge(register int u , register int v) { edge[++ cnt].v = v ; edge[cnt].nxt = head[u] ; head[u] = cnt ; return ; }//建边。
#define l(x) x << 1
#define r(x) x << 1 | 1
inline void Push_down(register int x , register int len) {
    laz[l(x)] += laz[x] ; laz[r(x)] += laz[x] ;
    Add[l(x)] += laz[x] * (len - (len >> 1)) ; Add[r(x)] += laz[x] * (len >> 1) ;
    Add[l(x)] %= p ; Add[r(x)] %= p ;
    laz[x] = 0 ; return ;
}
inline void Build(register int l , register int r , register int rt) {//建树
    if(l == r) { Add[rt] = a[l] ;  return ; }
    register int mid = l + r >> 1 ;
    Build(l , mid , l(rt)) ; Build(mid + 1 , r , r(rt)) ;
    Add[rt] = (Add[l(rt)] + Add[r(rt)]) % p ;
}
inline void Update(register int a , register int b , register int l  , register int r , register int rt , register int k) {//正常的线段树操作
    if(a <= l and r <= b) { laz[rt] += k ; Add[rt] += k * (r - l + 1) ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) Update(a , b , l , mid , l(rt) , k) ;
        if(b > mid) Update(a , b , mid + 1 , r , r(rt) , k) ;
        Add[rt] = (Add[l(rt)] + Add[r(rt)]) % p ;
    }
}
int res = 0 ;
inline void query(register int a , register int b , register int  l , register int r , register int rt) {
    if(a <= l and r <= b) { res = (res + Add[rt]) % p ; return ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) query(a , b , l , mid , l(rt)) ;
        if(b > mid) query(a , b , mid + 1 , r , r(rt)) ;
    }
}
inline int Query(register int a , register int b , register int l , register int r , register int rt) {//正常的线段树操作
    res = 0 ; query(a , b , l , r , rt) ;
    return res % p ;
}
inline void Upd_Range(register int x , register int y , register int k) {//链上修改
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
         Update(id[t[x]] , id[x] , 1 , n , 1 , k) ;
         x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    Update(id[x] , id[y] , 1 , n , 1 , k) ;
}
inline int Query_Range(register int x , register int y) {//链上查询
    int ans = 0 ;
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
        ans += Query(id[t[x]] , id[x] , 1 , n , 1) ;
        x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    ans += Query(id[x] , id[y] , 1 , n , 1) ;
    return ans % p ;
}
inline int Qson(register int x) { return Query(id[x] , id[x] + siz[x] - 1 , 1 , n , 1) ; } // 子树查询。
inline void Updson(register int x , register int k) { Update(id[x] , id[x] + siz[x] - 1 , 1 , n , 1 , k) ; return ; }
inline void Dfs1(register int x , register int f , register int deep) {
    dep[x] = deep ; fa[x] = f ; siz[x] = 1 ;
    int max_son = -1 ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        register int v = edge[i].v ;
        if(v == f) continue ;
        Dfs1(v , x , deep + 1) ;siz[x] += siz[v] ;
        if(siz[v] > max_son) max_son = siz[v] , son[x] = v ;
    }
}
int tot = 0 ;
inline void Dfs2(register int x , register int tf) {
    id[x] = ++ tot ; a[tot] = w[x] ; t[x] = tf ; // a 数组重新赋值
    if(! son[x]) return ;
    Dfs2(son[x] , tf) ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        int v = edge[i].v ;
        if(v == fa[x] or v == son[x]) continue ;
        Dfs2(v , v) ;
    }
}
signed main() {
    n = read() ; m = read() ; r = read() ; p = read() ;
    for(register int i = 1 ; i <= n ; i ++) w[i] = read() ;
    for(register int i = 1 ; i <= n - 1 ; i ++) {
        int u = read() , v = read() ;
        Add_Edge(u , v) ;
        Add_Edge(v , u) ;
    }
    Dfs1(r , 0 , 1) ; Dfs2(r , r) ; Build(1 , n , 1) ;
    for( ; m -- ; ) {
        register int opt = read() ;
        if(opt == 1) {
            register int x = read() , y = read() , k = read() ;
            Upd_Range(x , y , k % p) ;
        }
        if(opt == 2) {
            register int x = read() , y = read() ;
            printf("%d\n" , Query_Range(x , y)) ;
        }
        if(opt == 3) {
            register int x = read() , y = read() ;
            Updson(x , y) ;
        }
        if(opt == 4) { printf("%d\n" , Qson(read())) ; }
    }
    return 0 ;
}

[USACO11DEC]牧草种植Grass Planting

链上修改 单点查询。
对于这个题目 要修改的是边的权值。
这里有一个技巧 是 把边的权值下传给点 然后把最上面的点忽略掉。(易证 这样是可以的。)
然后进行基本的操作就可以了。

#include<bits/stdc++.h>
using namespace std ;
#define int long long
#define fi first
#define se second
#define pb push_back
inline int read() {
    register int x = 0 , f = 1 ;
    register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
}
template < typename T > inline bool cmax(T & x , T y) {
    return x < y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cmin(T & x , T y) {
    return x > y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cabs(T & x) {
    return x > 0 ? 1 : (x = - x) , 0 ;
}
inline int QP(int x , int y , int Mod) {
    int ans = 1 ;
    for( ; y ; y >>= 1 , x = (x * x) % Mod)
        if(y & 1) ans = (ans * x) % Mod ;
    return ans ;
}
int n , m ;
struct node {
  int v , nxt ;
} ;
const int N = 1e5 + 10 ;
node e[N << 1] ;
int head[N] , cnt = 0 ;
inline void Add(int u , int v) {
  e[++ cnt].v = v ;
  e[cnt].nxt = head[u] ;
  head[u] = cnt ;
  return ;
}
int fa[N] ;
int top[N] ;
int d[N] ;
int size[N] ;
int son[N] ;
int id[N] , idx = 0 ;
int a[N] ;
int sum[N << 2] ;
inline void build(int l , int r , int rt) {
  if(l == r) {
    sum[rt] = a[l] ;
    return ;
  }
  int mid = l + r >> 1 ;
  build(l , mid , rt << 1) ;
  build(mid + 1 , r , rt << 1 | 1) ;
}
int tag[N << 2] ;
inline void Push_down(int rt , int l , int r) {
  if(! tag[rt]) return ;
  tag[rt << 1] += tag[rt] ;
  tag[rt << 1 | 1] += tag[rt] ;
  int mid = l + r >> 1 ;
  sum[rt << 1] += tag[rt] * (mid - l + 1) ;
  sum[rt << 1 | 1] += tag[rt] * (r - mid) ;
  tag[rt] = 0 ;
  return ;
}
inline void Update(int a , int b , int l , int r , int rt) {
  if(a <= l && r <= b) { sum[rt] += (r - l + 1) ; tag[rt] ++ ; return ; }
  Push_down(rt , l , r) ;
  int mid = l + r >> 1 ;
  if(a <= mid) Update(a , b , l , mid , rt << 1) ;
  if(b > mid) Update(a , b , mid + 1 , r , rt << 1 | 1) ;
  sum[rt] = sum[rt << 1] + sum[rt << 1 | 1] ;
}
inline int Query(int a , int b , int l , int r , int rt) {
  if(a <= l && r <= b) return sum[rt] ;
  Push_down(rt , l , r) ;
  int mid = l + r >> 1 , ans = 0 ;
  if(a <= mid) ans += Query(a , b , l , mid , rt << 1) ;
  if(b > mid) ans += Query(a , b , mid + 1 , r , rt << 1 | 1) ;
  return ans ;
}
inline void Dfs(int u) {
  size[u] = 1 ;
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ;
    if(v == fa[u]) continue ;
    fa[v] = u ;
    d[v] = d[u] + 1 ;
    Dfs(v) ;
    size[u] += size[v] ;
    if(size[son[u]] < size[v]) son[u] = v ;
  }
}
inline void Dfs2(int u , int t) {
  top[u] = t ;
  a[idx] = 0 ;
  id[u] = ++ idx ;
  if(! son[u]) return ;
  Dfs2(son[u] , t) ;
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ;
    if(v ^ fa[u] && v ^ son[u]) Dfs2(v , v) ;
  }
}
inline void Change(int x , int y) {
  int fx = top[x] ;
  int fy = top[y] ;
  while(fx ^ fy) {
    if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
    Update(id[fx] , id[x] , 1 , n , 1) ;
    x = fa[fx] ;
    fx = top[x] ;
  }
  if(d[x] > d[y]) swap(x , y) ;
  Update(id[x] + 1 , id[y] , 1 , n , 1) ;
}
inline int Query_Range(int x , int y) {
  int ans = 0 ;
  int fx = top[x] ;
  int fy = top[y] ;
  while(fx ^ fy) {
    if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
    ans += Query(id[fx] , id[x] , 1 , n , 1) ;
    x = fa[fx] ;
    fx = top[x] ;
  }
  if(d[x] > d[y]) swap(x , y) ;
  ans += Query(id[x] + 1 , id[y] , 1 , n , 1) ;
  return ans ;
}
signed main() {
  n = read() ; m = read() ;
  for(register int i = 1 ; i <= n - 1 ; i ++) {
    int u = read() , v = read() ;
    Add(u , v) ;
    Add(v , u) ;
  }
  Dfs(1) ;
  Dfs2(1 , 0) ;
  build(1 , n , 1) ;
  for(register int i = 1 ; i <= m ; i ++) {
    register char c = getchar() ;
    while(c != 'P' && c != 'Q') c = getchar() ;
    int u = read() , v = read() ;
    if(c == 'P') Change(u , v) ;
    else printf("%lld\n" , Query_Range(u , v)) ;
  }
    return 0 ;
}

[USACO15DEC]最大流Max Flow

树剖是可以求LCA的 以这题为例。
这题是需要把压力给两个点 两个点加上1 然后这两个点的\(LCA\)减掉1 因为这个点也需要加上1 所以我们只能把这个1在\(fa_LCA\)减掉
这样递归到最底下 然后一直往上加 显然就是这棵树的权值

#include<bits/stdc++.h>
using namespace std ;
#define int long long
#define fi first
#define se second
#define pb push_back
inline int read() {
    register int x = 0 , f = 1 ;
    register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
}
template < typename T > inline bool cmax(T & x , T y) {
    return x < y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cmin(T & x , T y) {
    return x > y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cabs(T & x) {
    return x > 0 ? 1 : (x = - x) , 0 ;
}
inline int QP(int x , int y , int Mod) {
    int ans = 1 ;
    for( ; y ; y >>= 1 , x = (x * x) % Mod)
        if(y & 1) ans = (ans * x) % Mod ;
    return ans ;
}
int n ;
struct node {
  int v , nxt ;
} ;
const int N = 50000 + 5 ;
node e[N << 1] ;
int head[N] , cnt = 0 ;
inline void Add(int u , int v) {
  e[++ cnt].v = v ;
  e[cnt].nxt = head[u] ;
  head[u] = cnt ;
  return ;
}
int fa[N] ;
int size[N] ;
int son[N] ;
int top[N] ;
int d[N] ;
inline void Dfs(int u) {
  size[u] = 1 ;
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ;
    if(v == fa[u]) continue ;
    fa[v] = u ;
    d[v] = d[u] + 1 ;
    Dfs(v) ;
    size[u] += size[v];
    if(size[v] > size[son[u]]) son[u] = v ;
  }
}
inline void Dfs2(int u , int t) {
  top[u] = t ;
  if(! son[u]) return ;
  Dfs2(son[u] , t) ;
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ;
    if(v ^ fa[u] && v ^ son[u]) Dfs2(v , v) ;
  }
}
inline int Lca(int x , int y) {//树剖求LCA
  int fx = top[x] , fy = top[y] ;
  while(fx ^ fy) {
    if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
    x = fa[fx] ;
    fx = top[x] ;
  }
  if(d[x] > d[y]) swap(x , y) ;
  return x ;
}
int ad[N] ;
int ans = 0 ;
inline void Getans(int u , int father) { // 不断往下找 然后更新答案
  for(register int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ;
    if(v == father) continue ;
    Getans(v , u) ;
    ad[u] += ad[v] ;
  }
  ans = max(ans , ad[u]) ;
}
signed main() {
  n = read() ; int k = read() ;
  for(register int i = 1 ; i <= n - 1 ;  i ++) {
    int u = read() , v = read() ;
    Add(u , v) ;
    Add(v , u) ;
  }
  Dfs(1) ;
  Dfs2(1 , 0) ;
  for(register int i = 1 ; i <= k ; i ++) {
    int u = read() , v = read() ;
    int lca = Lca(u , v) ;
    ad[u] ++ ;
    ad[v] ++ ;
    ad[lca] -- ;
    ad[fa[lca]] -- ;
  }// 树上差分
  Getans(1 , 0) ;
  printf("%lld" , ans) ;
    return 0 ;
}

[HEOI2016/TJOI2016]树

技巧题
这题是可以在链上二分的 但是主流做法好像不是这个?
链上二分要注意是反过来的。。

#include<bits/stdc++.h>
using namespace std ;
#define int long long
#define fi first
#define se second
#define pb push_back
inline int read() {
    register int x = 0 , f = 1 ;
    register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
}
template < typename T > inline bool cmax(T & x , T y) {
    return x < y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cmin(T & x , T y) {
    return x > y ? (x = y) , 1 : 0 ;
}
template < typename T > inline bool cabs(T & x) {
    return x > 0 ? 1 : (x = - x) , 0 ;
}
inline int QP(int x , int y , int Mod) {
    int ans = 1 ;
    for( ; y ; y >>= 1 , x = (x * x) % Mod)
        if(y & 1) ans = (ans * x) % Mod ;
    return ans ;
}
int n , m ;
struct node {
    int v , nxt ;
} ;
const int N = 1e5 + 10 ;
node e[N << 1] ;
int head[N] , cnt = 0 ;
inline void Add(int u , int v) { e[++ cnt].v = v , e[cnt].nxt = head[u] , head[u] = cnt ; }
int fa[N] , size[N] , son[N] , d[N] ;
inline void Dfs(int u) { size[u] = 1 ; for(register int i = head[u] ; i ; i = e[i].nxt) {
        int v = e[i].v ; if(v == fa[u]) continue ;
        d[v] = d[u] + 1 , fa[v] = u , Dfs(v) , size[u] += size[v] ;
        if(size[v] > size[son[u]]) son[u] = v ;
    }
}
int top[N] , id[N] , idx = 0 , f[N] ;
inline void Dfs2(int u , int t) { id[u] = ++ idx , top[u] = t , f[idx] = u ;
    if(! son[u]) return ; Dfs2(son[u] , t) ;
    for(register int i = head[u] ; i ; i = e[i].nxt) {
        int v = e[i].v ; if(v ^ fa[u] && v ^ son[u]) Dfs2(v , v) ;
    }
}
int sum[N << 2] ;
inline void build(int l , int r , int rt){
    if(l == r) { sum[rt] = 0 ; return ; }
    int mid = l + r >> 1 ; build(l , mid , rt << 1) , build(mid + 1 , r , rt << 1 | 1) ;
}
inline void upd(int x , int l , int r , int rt) {
    if(l == r) { sum[rt] = 1 ; return ; }
    int mid = l + r >> 1 ;
    if(x <= mid) upd(x , l , mid , rt << 1) ; else upd(x , mid + 1 , r , rt << 1 | 1) ;
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1] ;
}
inline int query(int a , int b , int l , int r , int rt) {
    if(a <= l && r <= b) { return sum[rt] ; }
    int mid = l + r >> 1 , ans = 0 ;
    if(a <= mid) ans += query(a , b , l , mid , rt << 1) ;
    if(b > mid) ans += query(a , b ,  mid + 1 , r , rt << 1 | 1) ;
    return ans ;
}
inline int chk(int l , int r) { // 二分找答案
    if(l == r) return l ;
    int mid = l + r >> 1 ;
    if(query(mid + 1 , r , 1 , n , 1)) return chk(mid + 1 , r) ; // 反过来。
    else return chk(l , mid) ;
}
inline int Find(int x , int y = 1) { // 求答案 因为最近的点一定是在 1 ~ x 的这条路径上。
    int fx = top[x] , fy = top[y] ;
    while(fx ^ fy) {
        if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
        int q = query(id[fx] , id[x] , 1 , n , 1) ;
        if(q) return chk(id[fx] , id[x]) ;
        else { x = fa[fx] , fx = top[x] ; continue ; }
    }
    if(d[x] > d[y]) swap(x , y) ;
    return chk(id[x] , id[y]) ;
}
signed main() {
    n = read() ; m = read() ;
    for(register int i = 1 ; i <= n - 1 ; i ++) { int u = read() , v = read() ; Add(u , v) , Add(v , u) ;}
    Dfs(1) , Dfs2(1 , 0) , build(1 , n , 1) ;
    for(register int i = 1 ; i <= m ; i ++) {
        register char c = getchar() ;
        while(c != 'C' && c != 'Q') c = getchar() ;
        int x = read() ;
        if(c == 'C') upd(id[x] , 1 , n , 1) ;
        if(c == 'Q') printf("%lld\n" , f[Find(x)]) ;
    }
    return 0 ;
}

[国家集训队]旅游
sol

//Isaunoya
#include<bits/stdc++.h>
using namespace std ;
inline int read() {
    register int x = 0 ;
    register int f = 1 ;
    register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
}
int st[105] ;
template < typename T > inline void write(T x , char c = '\n') {
    int tp = 0 ;
    if(x == 0) return (void) puts("0") ;
    if(x < 0) putchar('-') , x = -x ;
    for( ; x ; x /= 10) st[++ tp] = x % 10 ;
    for( ; tp ; tp --) putchar(st[tp] + '0') ;
    putchar(c) ;
}
//#define Online_Judge
//#define int long long
#define swap(x , y) x ^= y ^= x ^= y
int n ;
const static int N = 200000 + 5 ;
int a[N] ;
namespace SegTree {
    struct Node {
        int mn ; // the min
        int mx ; // the max
        int add ; // the sum
        int lazy ; // the sign
    };
    Node t[N << 2] ;
    inline void Push_down(int rt) {
        if(t[rt].lazy) {
            t[rt << 1].add = - t[rt << 1].add ;
            t[rt << 1 | 1].add = - t[rt << 1 | 1].add ;
            t[rt << 1].lazy ^= 1 ;
            t[rt << 1 | 1].lazy ^= 1 ;
            swap(t[rt << 1].mx , t[rt << 1].mn) ;
            swap(t[rt << 1 | 1].mx , t[rt << 1 | 1].mn) ;
            t[rt << 1].mx = - t[rt << 1].mx ;
            t[rt << 1].mn = - t[rt << 1].mn ;
            t[rt << 1 | 1].mx = - t[rt << 1 | 1].mx ;
            t[rt << 1 | 1].mn = - t[rt << 1 | 1].mn ;
            t[rt].lazy = 0 ;
        }
        return ;
    }
    //==============================================push_down
    inline void Push_Up(int rt) {
        t[rt].add = t[rt << 1].add + t[rt << 1 | 1].add ;
        t[rt].mx = max(t[rt << 1].mx , t[rt << 1 | 1].mx) ;
        t[rt].mn = min(t[rt << 1].mn , t[rt << 1 | 1].mn) ;
        return ;
    }
    //==============================================push_up
    inline void build(int l , int r , int rt) {
        if(l == r) {
            t[rt].add = t[rt].mn = t[rt].mx = a[l] ;
            return ;
        }
        int mid = l + r >> 1 ;
        build(l , mid , rt << 1) ;
        build(mid + 1 , r , rt << 1 | 1) ;
        Push_Up(rt) ;
    }
    //==============================================build
    inline void Add(int x , int l , int r , int rt , int val) {
        if(l == r) {
            t[rt].add = t[rt].mn = t[rt].mx = val ;
            return ;
        }
        int mid = l + r >> 1 ;
        Push_down(rt) ;
        if(x <= mid) Add(x , l , mid , rt << 1 , val) ;
        else Add(x , mid + 1 , r , rt << 1 | 1 , val) ;
        Push_Up(rt) ;
    }
    //==============================================change x - > val
    inline void Change(int a , int b , int l , int r , int rt) {
        if(a > r || b < l) return ;
        if(a <= l && r <= b) {
            t[rt].add = - t[rt].add ;
            t[rt].lazy ^= 1 ;
            swap(t[rt].mx , t[rt].mn) ;
            t[rt].mx = - t[rt].mx ;
            t[rt].mn = - t[rt].mn ;
            return ;
        }
        int mid = l + r >> 1 ;
        Push_down(rt) ;
        Change(a , b , l , mid , rt << 1) ;
        Change(a , b , mid + 1 , r , rt << 1 | 1) ;
        Push_Up(rt) ;
    }
    //===============================================change x - > -x
    inline int Sum(int a , int b , int l , int r , int rt) {
        if(a > r || b < l) return 0 ;
        if(a <= l && r <= b) return t[rt].add ;
        int mid = l + r >> 1 ;
        Push_down(rt) ;
        int ans = 0 ;
        ans += Sum(a , b , l , mid , rt << 1 ) ;
        ans += Sum(a , b , mid + 1 , r , rt << 1 | 1) ;
        Push_Up(rt) ;
        return ans ;
    }
    //====================================================== a - > b sum
    inline int Min(int L , int R , int l , int r , int rt) {
        if(L > r || R < l) return INT_MAX ;
        if(L <= l && r <= R) return t[rt].mn ;
        int ans = INT_MAX ;
        int mid = l + r >> 1 ;
        Push_down(rt) ;
        if(L <= mid) ans = min(ans , Min(L , R , l , mid , rt << 1)) ;
        if(R > mid) ans = min(ans , Min(L , R, mid + 1 , r , rt << 1 | 1)) ;
        Push_Up(rt) ;
        return ans ;
    }
    //====================================================== a - > b min
    inline int Max(int L , int R , int l , int r , int rt) {
        if(L > r || R < l) return INT_MIN ;
        if(L <= l && r <= R) return t[rt].mx ;
        int ans = INT_MIN ;
        int mid = l + r >> 1 ;
        Push_down(rt) ;
        if(L <= mid) ans = max(ans , Max(L , R , l , mid , rt << 1)) ;
        if(R > mid) ans = max(ans , Max(L , R, mid + 1 , r , rt << 1 | 1)) ;
        Push_Up(rt) ;
        return ans ;
    }
    //====================================================== a - > b max
}
//===========================================================SegTree
namespace SLPF {
    struct node {
        int v ;
        int nxt ;
        int w ;
    };
    int fa[N] ; int id[N] ; int son[N] ;
    int size[N] ; int d[N] ; int top[N] ;
    int fst[N] ;
    node e[N << 1] ;
    int tot = 0 ;
    int head[N] ; int cnt = 0 ;
    inline void Add_Edge(int u , int v , int w) {
        e[++ cnt].v = v ;
        e[cnt].nxt = head[u] ;
        e[cnt].w = w ;
        head[u] = cnt ;
        return ;
    }
    inline void Dfs1(int u) {
        size[u] = 1 ;
        for(register int i = head[u] ; i ; i = e[i].nxt) {
            int v = e[i].v ;
            if(v ^ fa[u]) {
                d[v] = d[u] + 1 ;
                fa[v] = u ;
                fst[v] = e[i].w ;
                Dfs1(v) ;
                size[u] += size[v] ;
                if(size[v] > size[son[u]]) son[u] = v ;
            }
        }
    }
    inline void Dfs2(int u , int t) {
        id[u] = ++ tot ;
        top[u] = t ;
        a[tot] = fst[u] ;
        if(son[u]) Dfs2(son[u] , t) ;
        for(register int i = head[u] ; i ; i = e[i].nxt) {
            int v = e[i].v ;
            if(v ^ fa[u] && v ^ son[u]) Dfs2(v , v) ;
        }
    }
    //========================================================Dfs1 && Dfs2

    inline void Change_Range(int x , int y) {
        int fx = top[x] ;
        int fy = top[y] ;
        while(fx ^ fy) {
            if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
            SegTree::Change(id[fx] , id[x] , 1 , tot , 1) ;
            x = fa[fx] ;
            fx = top[x] ;
        }
        if(id[x] > id[y]) swap(x , y) ;
        SegTree::Change(id[x] + 1 , id[y] , 1 , tot , 1) ;
    }
    inline int Query_Sum(int x , int y) {
        int fx = top[x] ;
        int fy = top[y] ;
        int ans = 0 ;
        while(fx ^ fy) {
            if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
            ans += SegTree::Sum(id[fx] , id[x] , 1 , tot , 1) ;
            x = fa[fx] ;
            fx = top[x] ;
        }
        if(id[x] > id[y]) swap(x , y) ;
        ans += SegTree::Sum(id[x] + 1 , id[y] , 1 , tot , 1) ;
        return ans ;
    }
    inline int Query_Min(int x , int y) {
        int fx = top[x] ;
        int fy = top[y] ;
        int ans = INT_MAX ;
        while(fx ^ fy) {
            if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
            ans = min(ans , SegTree::Min(id[fx] , id[x] , 1 , tot , 1)) ;
            x = fa[fx] ;
            fx = top[x] ;
        }
        if(id[x] > id[y]) swap(x , y) ;
        ans = min(ans , SegTree::Min(id[x] + 1 , id[y] , 1 , tot , 1)) ;
        return ans ;
    }
    inline int Query_Max(int x , int y) {
        int fx = top[x] ;
        int fy = top[y] ;
        int ans = INT_MIN ;
        while(fx ^ fy) {
            if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
            ans = max(ans , SegTree::Max(id[fx] , id[x] , 1 , tot , 1)) ;
            x = fa[fx] ;
            fx = top[x] ;
        }
        if(id[x] > id[y]) swap(x , y) ;
        ans = max(ans , SegTree::Max(id[x] + 1 , id[y] , 1 , tot , 1)) ;
        return ans ;
    }
}
using namespace SLPF ;
inline int getopt() {
    string s = "" ;
    register char c = getchar() ;
    while(isspace(c)) c = getchar() ;
    while(! isspace(c)) {
        s += c ;
        c = getchar() ;
    }
    if(s == "C") return 0 ;
    if(s == "N") return 1 ;
    if(s == "SUM") return 2 ;
    if(s == "MAX") return 3 ;
    if(s == "MIN") return 4 ;
}
signed main() {
#ifdef Online_Judge
    freopen("testdata.in" , "r" , stdin) ;
    freopen("testdata2.out" , "w" , stdout) ;
#endif
    n = read() ;
    for(register int i = 1 ; i <= n - 1 ; i ++) {
        int u = read() , v = read() , w = read() ;
        u ++ , v ++ ;
        Add_Edge(u , v , w) ;
        Add_Edge(v , u , w) ;
    }
    Dfs1(1) ;
    Dfs2(1 , 0) ;
    SegTree::build(1 , n , 1) ;
    for(register int t = read() ; t -- ; ) {
        int opt = getopt() ;
//      write(opt) ;
        if(opt == 0) {
            int x = read() , y = read() ;
            x ++ ;
            SegTree::Add(id[x] , 1 , n , 1 , y) ;
        }
        if(opt == 1) {
            int x = read() , y = read() ;
            x ++ , y ++ ;
            Change_Range(x , y) ;
        }
        if(opt == 2) {
            int x = read() , y = read() ;
            x ++ , y ++ ;
            write(Query_Sum(x , y)) ;
        }
        if(opt == 3) {
            int x = read() , y = read() ;
            x ++ , y ++ ;
            write(Query_Max(x , y)) ;
        }
        if(opt == 4) {
            int x = read() , y = read() ;
            x ++ , y  ++ ;
            write(Query_Min(x , y)) ;
        }
    }
    return 0 ;
}

[ZJOI2008]树的统计

与模板题差不多。。

//Isaunoya
#include <bits/stdc++.h>
using namespace std ;
inline int read() { register int x = 0 ; register int f = 1 ; register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
} int st[105] ;
template < typename T > inline void write(T x , char c = '\n') { int tp = 0 ;
    if(x == 0) return (void) puts("0") ;
    if(x < 0) putchar('-') , x = -x ;
    for( ; x ; x /= 10) st[++ tp] = x % 10 ;
    for( ; tp ; tp --) putchar(st[tp] + '0') ;
    putchar(c) ;
}
int n , m , r , p ;
const int N = 10000000 + 5 ;
struct E{ int v ;int nxt ; } ;
E edge[N << 1] ;
int a[N] ; int fa[N] ; int w[N] ; int id[N] ; int son[N] ;
int cnt = 0 ; int head[N] ; int Add[N << 2] , laz[N << 2] ;
int mx[N << 2] ;
int dep[N] , siz[N] , t[N] ;
inline void Add_Edge(register int u , register int v) { edge[++ cnt].v = v ; edge[cnt].nxt = head[u] ; head[u] = cnt ; return ; }
#define l(x) x << 1
#define r(x) x << 1 | 1
inline void Push_down(register int x , register int len) {
    laz[l(x)] += laz[x] ; laz[r(x)] += laz[x] ;
    Add[l(x)] += laz[x] * (len - (len >> 1)) ; Add[r(x)] += laz[x] * (len >> 1) ;
    Add[l(x)] %= p ; Add[r(x)] %= p ;
    laz[x] = 0 ; return ;
}
inline void Build(register int l , register int r , register int rt) {
    if(l == r) { mx[rt] = Add[rt] = a[l] ;  return ; }
    register int mid = l + r >> 1 ;
    Build(l , mid , l(rt)) ; Build(mid + 1 , r , r(rt)) ;
    Add[rt] = (Add[l(rt)] + Add[r(rt)])  ;
    mx[rt] = max(mx[rt << 1] , mx[rt << 1 | 1]) ;
}
inline void Update(register int a , register int b , register int l  , register int r , register int rt , register int k) {
    if(a <= l and r <= b) { laz[rt] += k ; Add[rt] += k * (r - l + 1) ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) Update(a , b , l , mid , l(rt) , k) ;
        if(b > mid) Update(a , b , mid + 1 , r , r(rt) , k) ;
        Add[rt] = (Add[l(rt)] + Add[r(rt)])  ;
        mx[rt] = max(mx[rt << 1] , mx[rt << 1 | 1]) ;
    }
}
inline void upd(int x , int l , int r , int rt , int v) {
    if(l == r) {
        mx[rt] = Add[rt] = v ;
        return ;
    }
    int mid = l + r >> 1 ;
    if(x <= mid) upd(x , l , mid , rt << 1 , v) ;
    else upd(x , mid + 1 , r , rt << 1 | 1 , v) ;
    Add[rt] = (Add[l(rt)] + Add[r(rt)])  ;
    mx[rt] = max(mx[rt << 1] , mx[rt << 1 | 1]) ;
}
int res = 0 ;
inline void query(register int a , register int b , register int  l , register int r , register int rt) {
    if(a <= l and r <= b) { res = (res + Add[rt])  ; return ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) query(a , b , l , mid , l(rt)) ;
        if(b > mid) query(a , b , mid + 1 , r , r(rt)) ;
    }
}
inline int Query(register int a , register int b , register int l , register int r , register int rt) {
    res = 0 ; query(a , b , l , r , rt) ;
    return res  ;
}
inline int Query_Max(int a , int b , int l , int r , int rt) {
    if(a <= l && r <= b) return mx[rt] ;
    int mid = l + r >> 1 ;
    int ans = INT_MIN ;
    if(a <= mid) ans = max(ans , Query_Max(a , b , l , mid , rt << 1)) ;
    if(b > mid) ans = max(ans , Query_Max(a , b , mid + 1 , r , rt << 1 | 1)) ;
    return ans ;
}
inline void Upd_Range(register int x , register int y , register int k) {
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
         Update(id[t[x]] , id[x] , 1 , n , 1 , k) ;
         x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    Update(id[x] , id[y] , 1 , n , 1 , k) ;
}
inline int Query_Range(register int x , register int y) {
    int ans = 0 ;
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
        ans += Query(id[t[x]] , id[x] , 1 , n , 1) ;
        x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    ans += Query(id[x] , id[y] , 1 , n , 1) ;
    return ans  ;
}
inline int Query_Max_Range(register int x , register int y) {
    int ans = INT_MIN ;
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
        ans = max(ans , Query_Max(id[t[x]] , id[x] , 1 , n , 1)) ;
        x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    ans = max(ans , Query_Max(id[x] , id[y] , 1 , n , 1)) ;
    return ans  ;
}
inline int Qson(register int x) { return Query(id[x] , id[x] + siz[x] - 1 , 1 , n , 1) ; }
inline void Updson(register int x , register int k) { Update(id[x] , id[x] + siz[x] - 1 , 1 , n , 1 , k) ; return ; }
inline void Dfs1(register int x , register int f , register int deep) {
    dep[x] = deep ; fa[x] = f ; siz[x] = 1 ;
    int max_son = -1 ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        register int v = edge[i].v ;
        if(v == f) continue ;
        Dfs1(v , x , deep + 1) ;siz[x] += siz[v] ;
        if(siz[v] > max_son) max_son = siz[v] , son[x] = v ;
    }
}
int tot = 0 ;
inline void Dfs2(register int x , register int tf) {
    id[x] = ++ tot ; a[tot] = w[x] ; t[x] = tf ;
    if(! son[x]) return ;
    Dfs2(son[x] , tf) ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        int v = edge[i].v ;
        if(v == fa[x] or v == son[x]) continue ;
        Dfs2(v , v) ;
    }
}
inline int getopt() {
    string s = "" ; register char c = getchar() ;
    while(isspace(c)) c = getchar() ;
    while(! isspace(c)) {
        s += c ;
        c = getchar() ;
    }
    if(s == "CHANGE") return 0 ;
    if(s == "QMAX") return 1 ;
    if(s == "QSUM") return 2 ;
}
signed main() {
    n = read() ;
    for(register int i = 1 ; i <= n - 1 ; i ++) {
        int u = read() , v = read() ;
        Add_Edge(u , v) ;
        Add_Edge(v , u) ;
    }for(register int i = 1 ; i <= n ; i ++) w[i] = read() ;
    m = read() ; r = 1 ;
    Dfs1(r , 0 , 1) ; Dfs2(r , r) ; Build(1 , n , 1) ;
    for( ; m -- ; ) {
        int opt = getopt() ;
//        cout  << opt ;
        int u = read() , v = read() ;
        if(opt == 0) {
            upd(id[u] , 1 , n , 1 , v) ;
        }
        if(opt == 1) {
            write(Query_Max_Range(u , v)) ;
        }
        if(opt == 2) {
            write(Query_Range(u , v)) ;
        }
    }
    return 0 ;
}

上面都是用线段树维护的序列 其实树剖不一定要用 线段树 只要是数据结构都可以
比如说ODT 什么的 如果不会ODT也没什么关系 你会用线段树当然更好 ODT 是可以卡掉的 但是在树上问题 通常不容易卡掉。

[NOI2015]软件包管理器
因为 软件之间是有依赖关系的

  • 每次安装软件 就把根节点到x软件路径上的值全部变为1
  • 同理 每次卸载软件 就把x以及它的子树的值变为0
//Isaunoya
#include<bits/stdc++.h>
using namespace std ;
inline int read() { register int x = 0 ; register int f = 1 ; register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
} int st[105] ;
template < typename T > inline void write(T x , char c = '\n') { int tp = 0 ;
    if(x == 0) return (void) puts("0") ;
    if(x < 0) putchar('-') , x = -x ;
    for( ; x ; x /= 10) st[++ tp] = x % 10 ;
    for( ; tp ; tp --) putchar(st[tp] + '0') ;
    putchar(c) ;
}
//#define Online_Judge

struct node {
    int l ; int r ;
    mutable int val ;
    bool operator < (const node & x) const {
        return l < x.l ;
    }
};
set < node > s ;

#define slt set < node > :: iterator
inline slt Split(int pos) {
    slt it = s.lower_bound((node) {pos}) ;
    if(it != s.end() && it -> l == pos) return it ;
    -- it ;
    int l = it -> l ;
    int r = it -> r ;
    int val = it -> val ;
    s.erase(it) ;
    s.insert({l , pos - 1 , val}) ;
    return s.insert({pos , r , val}).first ;
}
inline int Assign(int l , int r , int val) {
    slt it2 = Split(r + 1) ;
    slt it1 = Split(l) ;
    int sum = 0 ;
    int sum2 = (r - l + 1) * val  ;
    for(slt it = it1 ; it != it2 ; it ++) sum += (it -> r - it -> l + 1) * it -> val ;
    s.erase(it1 , it2) ;
    s.insert({l , r , val}) ;
    return abs(sum - sum2) ;
}
int n ;
struct Node {
    int v ;
    int nxt ;
};
const int N = 1e5 + 10 ;
Node e[N << 1] ;
int cnt = 0 ;
int head[N] ;
inline void Add(int u , int v) {
    e[++ cnt].v = v ;
    e[cnt].nxt = head[u] ;
    head[u] = cnt ;
    return ;
}
int top[N] ;
int id[N] ; int size[N] ;
int d[N] ; int idx = 0 ;
int fa[N] ; int son[N] ;
inline void Dfs1(int u) {
    size[u] = 1 ;
    for(register int i = head[u] ; i ; i = e[i].nxt ) {
        int v = e[i].v ;
        if(v == fa[u]) continue ;
        d[v] = d[u] + 1 ;
        fa[v] = u ;
        Dfs1(v) ;
        size[u] += size[v] ;
        if(size[v] > size[son[u]]) son[u] = v ;
    }
    return ;
}
inline void Dfs2(int u , int t) {
    id[u] = ++ idx ;
    top[u] = t ;
    if(! son[u]) return ;
    Dfs2(son[u] , t) ;
    for(register int i = head[u] ; i ; i = e[i].nxt) {
        int v = e[i].v ;
        if((v ^ fa[u]) && (v ^ son[u])) Dfs2(v , v) ;
    }
}
inline int getopt() { string s = "" ;
    register char c = getchar() ;
    while(isspace(c)) c = getchar() ;
    while(! isspace(c)) {
        s += c ;
        c = getchar() ;
    }
    if(s == "install") return 1 ;
    if(s == "uninstall") return 0 ;
}
inline int Change_Range(int x , int y) {
    int fx = top[x] ;
    int fy = top[y] ;
    int ans = 0 ;
    while(fx ^ fy) {
        if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
        ans += Assign(id[fx] , id[x] , 1) ;
        x = fa[fx] ;
        fx = top[x] ;
    }
    if(id[x] > id[y]) swap(x , y) ;
    ans += Assign(id[x] , id[y] , 1) ;
    return ans ;
}
inline int Uninstall(int x) {
    return Assign(id[x] , id[x] + size[x] - 1 , 0) ;
}
signed main() {
#ifdef Online_Judge
    freopen("testdata.in" , "r" , stdin) ;
    freopen("testdata2.out" , "w" , stdout) ;
#endif
    n = read() ;
    s.insert({1 , n + 1 , 0}) ;
    for(register int i = 2 ; i <= n ; i ++) {
        int u = read() ; u ++ ;
        Add(u , i) ;
        Add(i , u) ;
    }
    Dfs1(1) ;
    Dfs2(1 , 1) ;
    for(register int t = read() ; t -- ; ) {
        int opt = getopt() ;
        if(opt == 1) {
            int x = read() ; x ++ ;
            write(Change_Range(x , 1)) ;
        }
        if(opt == 0) {
            int x = read() ; x ++ ;
            write(Uninstall(x)) ;
        }
    }
    return 0 ;
}

[SDOI2011]染色

这题也是个ODT
不过不同的是 这题是区间的颜色数量 连续的颜色算一次 这样的话 线段树还是很难维护的。(应该也可以。)

//Isaunoya
#include<bits/stdc++.h>
using namespace std ;
inline int read() {
    register int x = 0 ;
    register int f = 1 ;
    register char c = getchar() ;
    for( ; ! isdigit(c) ; c = getchar()) if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar()) x = (x << 1) + (x << 3) + (c & 15) ;
    return x * f ;
}
int st[105] ;
template < typename T > inline void write(T x , char c = '\n') {
    int tp = 0 ;
    if(x == 0) return (void) puts("0") ;
    if(x < 0) putchar('-') , x = -x ;
    for( ; x ; x /= 10) st[++ tp] = x % 10 ;
    for( ; tp ; tp --) putchar(st[tp] + '0') ;
    putchar(c) ;
}
//#define Online_Judge

struct node {
    int l ;
    int r ;
    mutable int val ;
    bool operator < (const node & x) const {
        return l < x.l ;
    }
};
set < node > s ;

#define slt set < node > :: iterator
inline slt Split(int pos) {
    slt it (-- s.upper_bound({pos})) ;
    if(it -> l == pos) return it ;
    int l = it -> l ;
    int r = it -> r ;
    int val = it -> val ;
    s.erase(it) ;
    s.insert({l , pos - 1 , val}) ;
    return s.insert({pos , r , val}).first ;
}
inline void Assign(int l , int r , int val) {
    slt itr = Split(r + 1) ;
    slt itl = Split(l) ;
    s.erase(itl , itr) ;
    s.insert({l , r , val}) ;
    return ;
}
int n ;
struct Node {
    int v ;
    int nxt ;
};
const int N = 1e5 + 10 ;
int a[N] ;
Node e[N << 1] ;
int cnt = 0 ;
int head[N] ;
inline void Add(int u , int v) {
    e[++ cnt].v = v ;
    e[cnt].nxt = head[u] ;
    head[u] = cnt ;
    return ;
}
int top[N] ;
int id[N] ;
int size[N] ;
int d[N] ;
int idx = 0 ;
int fa[N] ;
int son[N] ;
int fst[N] ;
inline void Dfs1(int u) {
    size[u] = 1 ;
    for(register int i = head[u] ; i ; i = e[i].nxt ) {
        int v = e[i].v ;
        if(v == fa[u]) continue ;
        d[v] = d[u] + 1 ;
        fa[v] = u ;
        Dfs1(v) ;
        size[u] += size[v] ;
        if(size[v] > size[son[u]]) son[u] = v ;
    }
    return ;
}
inline void Dfs2(int u , int t) {
    id[u] = ++ idx ;
    top[u] = t ;
    a[idx] = fst[u] ;
    if(! son[u]) return ;
    Dfs2(son[u] , t) ;
    for(register int i = head[u] ; i ; i = e[i].nxt) {
        int v = e[i].v ;
        if((v ^ fa[u]) && (v ^ son[u])) Dfs2(v , v) ;
    }
}
inline int getopt() {
    register char c = getchar() ;
    while(isspace(c)) c = getchar() ;
    return c == 'Q' ;
}
inline void Change_Range(int x , int y , int val) {
    int fx = top[x] ;
    int fy = top[y] ;
    while(fx ^ fy) {
        if(d[fx] < d[fy]) swap(x , y) , swap(fx , fy) ;
        Assign(id[fx] , id[x] , val) ;
        x = fa[fx] ;
        fx = top[x] ;
    }
    if(id[x] > id[y]) swap(x , y) ;
    Assign(id[x] , id[y] , val) ;
}
#define swap(x , y) x ^= y ^= x ^= y
inline int Query_Range(int x , int y) {
    int ans = 0 ;
    int lasta = 0 ;
    int lastb = 0 ;
    int fx = top[x] ;
    int fy = top[y] ;
    slt itl , itr ;
    while(fx ^ fy) {
        if(d[fx] > d[fy]) {
            itr = Split(id[x] + 1) , itl = Split(id[fx]) ;
            for(-- itr ; ; --itr) {
                if(itr -> val ^ lasta)
                    lasta = itr -> val , ++ ans ;
                if(itr == itl) break;
            }
            x = fa[fx] ;
            fx = top[x] ;
        } else {
//          swap(x , y) , swap(fx , fy) ;
            itr = Split(id[y] + 1) , itl = Split(id[fy]) ;
            for(-- itr ; ; --itr) {
                if(itr -> val ^ lastb)
                    lastb = itr -> val , ++ ans ;
                if(itr == itl) break;
            }
            y = fa[fy] ;
            fy = top[y] ;
        }
    }
    if(id[x] > id[y]) {
        itr = Split(id[x] + 1) , itl = Split(id[y]) ;
        for(-- itr ; ; --itr) {
            if(itr -> val ^ lasta)
                lasta = itr -> val , ++ ans ;
            if(itr == itl) break;
        }
    } else {
        itr = Split(id[y] + 1) , itl = Split(id[x]) ;
        for(-- itr ; ; --itr) {
            if(itr -> val ^ lastb)
                lastb = itr -> val , ++ ans ;
            if(itr == itl) break;
        }
    }
    return ans - (lasta == lastb) ;
}
signed main() {
#ifdef Online_Judge
    freopen("testdata.in" , "r" , stdin) ;
    freopen("testdata2.out" , "w" , stdout) ;
#endif
    n = read() ;
    int t = read() ;
    for(register int i = 1 ; i <= n ; i ++) fst[i] = read() ;
    for(register int i = 2 ; i <= n ; i ++) {
        int u = read() ;
        int v = read() ;
        Add(u , v) ;
        Add(v , u) ;
    }
    Dfs1(1) ;
    Dfs2(1 , 1) ;
    for(register int i = 1 ; i <= n ; i ++) s.insert({i , i , a[i]}) ;
    for( ; t -- ; ) {
        int opt = getopt() ;
        if(opt == 1) {
            int x = read() , y = read() ;
            write(Query_Range(x , y));
        }
        if(opt == 0) {
            int x = read() , y = read() , val = read() ;
            Change_Range(x , y , val) ;
        }
    }
    return 0 ;
}

[HAOI2015]树上操作

  • 操作 1 :把某个节点 x 的点权增加 a 。
  • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
  • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

很显然是个板子题啊。

// Isaunoya
#include <bits/stdc++.h>
using namespace std ;
inline int read() { register int res = 0 ; register int f = 1 ; register char c ;
#define gc c = getchar()
    while(isspace(gc)) ; if(c == '-') f = -1 , gc ;
    while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(gc)) ;
    return res * f ;
}
#define int long long
int n , m , r ;
const int N = 1000000 + 5 ;
struct E{ int v ;int nxt ; } ;
E edge[N << 1] ;
int a[N] ; int fa[N] ; int w[N] ; int id[N] ; int son[N] ;
int cnt = 0 ; int head[N] ; int Add[N << 2] , laz[N << 2] ;
int dep[N] , siz[N] , t[N] ;
inline void Add_Edge(register int u , register int v) { edge[++ cnt].v = v ; edge[cnt].nxt = head[u] ; head[u] = cnt ; return ; }
#define l(x) x << 1
#define r(x) x << 1 | 1
inline void Push_down(register int x , register int len) {
    laz[l(x)] += laz[x] ; laz[r(x)] += laz[x] ;
    Add[l(x)] += laz[x] * (len - (len >> 1)) ; Add[r(x)] += laz[x] * (len >> 1) ;
    laz[x] = 0 ; return ;
}
inline void Build(register int l , register int r , register int rt) {
    if(l == r) { Add[rt] = a[l] ;  return ; }
    register int mid = l + r >> 1 ;
    Build(l , mid , l(rt)) ; Build(mid + 1 , r , r(rt)) ;
    Add[rt] = (Add[l(rt)] + Add[r(rt)])  ;
}
inline void Update(register int a , register int b , register int l  , register int r , register int rt , register int k) {
    if(a <= l and r <= b) { laz[rt] += k ; Add[rt] += k * (r - l + 1) ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) Update(a , b , l , mid , l(rt) , k) ;
        if(b > mid) Update(a , b , mid + 1 , r , r(rt) , k) ;
        Add[rt] = (Add[l(rt)] + Add[r(rt)])  ;
    }
}
int res = 0 ;
inline void query(register int a , register int b , register int  l , register int r , register int rt) {
    if(a <= l and r <= b) { res = (res + Add[rt])  ; return ; }
    else {
        if(laz[rt]) Push_down(rt , r - l + 1) ;
        register int mid = l + r >> 1 ;
        if(a <= mid) query(a , b , l , mid , l(rt)) ;
        if(b > mid) query(a , b , mid + 1 , r , r(rt)) ;
    }
}
inline int Query(register int a , register int b , register int l , register int r , register int rt) {
    res = 0 ; query(a , b , l , r , rt) ;
    return res  ;
}
inline void Upd_Range(register int x , register int y , register int k) {
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
         Update(id[t[x]] , id[x] , 1 , n , 1 , k) ;
         x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    Update(id[x] , id[y] , 1 , n , 1 , k) ;
}
inline int Query_Range(register int x , register int y) {
    int ans = 0 ;
    while(t[x] != t[y]) {
        if(dep[t[x]] < dep[t[y]]) swap(x , y) ;
        ans += Query(id[t[x]] , id[x] , 1 , n , 1) ;
        x = fa[t[x]] ;
    }
    if(dep[x] > dep[y]) swap(x , y) ;
    ans += Query(id[x] , id[y] , 1 , n , 1) ;
    return ans ;
}
inline int Qson(register int x) { return Query(id[x] , id[x] + siz[x] - 1 , 1 , n , 1) ; }
inline void Updson(register int x , register int k) { Update(id[x] , id[x] + siz[x] - 1 , 1 , n , 1 , k) ; return ; }
inline void Dfs1(register int x , register int f , register int deep) {
    dep[x] = deep ; fa[x] = f ; siz[x] = 1 ;
    int max_son = -1 ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        register int v = edge[i].v ;
        if(v == f) continue ;
        Dfs1(v , x , deep + 1) ;siz[x] += siz[v] ;
        if(siz[v] > max_son) max_son = siz[v] , son[x] = v ;
    }
}
int tot = 0 ;
inline void Dfs2(register int x , register int tf) {
    id[x] = ++ tot ; a[tot] = w[x] ; t[x] = tf ;
    if(! son[x]) return ;
    Dfs2(son[x] , tf) ;
    for(register int i = head[x] ; i ; i = edge[i].nxt) {
        int v = edge[i].v ;
        if(v == fa[x] or v == son[x]) continue ;
        Dfs2(v , v) ;
    }
}
signed main() {
    n = read() ; m = read() ; r = 1 ;
    for(register int i = 1 ; i <= n ; i ++) w[i] = read() ;
    for(register int i = 1 ; i <= n - 1 ; i ++) {
        int u = read() , v = read() ;
        Add_Edge(u , v) ;
        Add_Edge(v , u) ;
    }
    Dfs1(r , 0 , 1) ; Dfs2(r , r) ; Build(1 , n , 1) ;
    for( ; m -- ; ) {
        register int opt = read() ;
        if(opt == 1) {
            int x = read() , a = read() ;
            Upd_Range(x , x , a) ;
        }
        if(opt == 2) {
            int x = read() , z = read() ;
            Updson(x , z) ;
        }
        if(opt == 3) {
            int x = read() ;
            printf("%lld\n" , Query_Range(x , r)) ;
        }
    }
    return 0 ;
}

原文地址:https://www.cnblogs.com/Isaunoya/p/11619823.html

时间: 2024-11-01 09:52:11

树链剖分(从入门到入土。)的相关文章

树链剖分模板+入门题 SPOJ - QTREE

题目链接:[点击进入](http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13013) 树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上.比如说下面这道题,就是将树分为重链和轻链然后映射到线段树上,然后再在线段树上进行查询和修改等操作.所以树链剖分的重点有两个,一是正确的将树分解成几段并映射到

bzoj-2243 2243: [SDOI2011]染色(树链剖分)

题目链接: 2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6267  Solved: 2291 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含

spoj 375 AND bzoj 1036 树链剖分

树链剖分的入门题目,自己写了下感觉还是挺好写的,不过真的有点长... spoj 375 边有权值: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = -999999999; 7 const int N = 10001; 8 int head[N]; 9 int sz[N]; 10 int depth[N]; 1

树链剖分入门-Hdu3966 Aragorn&#39;s Story

AC通道:http://acm.hdu.edu.cn/showproblem.php?pid=3966 [题目大意] 一棵树上每个点有权值,每次支持三种操作:给[a,b]路径上的所有节点的权值加上k,给[a,b]路径上的所有节点的权值减去k,以及询问a的权值. [分析] 这是一道树链剖分模板题. 树链剖分,就是将树化成了许多链,将这些链用数据结构保存起来,再去维护这个数据结构. 假设给的树就是一条链,这道题当然很好办:直接将链用线段树存了,因为[a,b]的路径在线段树上也是连续的一段,那么修改一

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

洛谷 P2590 树的统计 P3178 树上操作【树链剖分入门】

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连

树链剖分入门

这几天学了一个树链剖分,觉得还不是很难,这里我试着讲一讲吧. 首先,我认为树链剖分是把在树上一个节点一个节点的走改为按照某种规则跳,从而降低了时间复杂度. 那这是什么规则呢? 首先我们得知道什么是重链,知道什么是重链就得先知道什么是重儿子,重儿子就是子树较大的儿子.然后对于一个点,我们总是往他的重儿子走,这样就构成了重链,那么剩下的就是轻链. 放张图直观些 然后我们同样可以对树进行dfs,只不过重儿子优先,这样我们也得到了一个dfs序,于是我们把树上问题成功转化成了线性问题.接着就可以用线段树等

树链剖分 入门

什么是树链剖分? 树链剖分说白了就是将树的节点按照某种顺序编号,使其在特殊的链上编号连续(类似区间),方便用数据结构维护. 如何树链剖分? 树链剖分一般分为重链剖分和长链剖分,这里只介绍重链剖分(我也只会重链剖分). 重链剖分中有几个概念: 重儿子:一个节点的所有子节点中,以某个子节点为根的子树中节点数量最多的称为重儿子(如果最多的数量相同随便取一个). 轻儿子:一个节点的所有子节点中,不是重儿子的节点就是轻儿子(这不是废话吗). 重边:父节点与重儿子所连成的边. 轻边:父节点与轻儿子所连成的边

HDU 3966 Aragorn&amp;#39;s Story(树链剖分)

HDU Aragorn's Story 题目链接 树抛入门裸题,这题是区间改动单点查询,于是套树状数组就OK了 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N = 50005; inline int lowbit(int x) {return x&(-x);} int dep