[模板]树链剖分
题目描述
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:
操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
输入输出格式
输入格式:
第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。
接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。
接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)
接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:
操作1: 1 x y z
操作2: 2 x y
操作3: 3 x z
操作4: 4 x
输出格式:
输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)
输入输出样例
输入样例#1:
5 5 2 24 7 3 7 8 0 1 2 1 5 3 1 4 1 3 4 2 3 2 2 4 5 1 5 1 3 2 1 3
输出样例#1:
2 21
说明
时空限制:1s,128M
数据规模:
对于100%的数据: N≤105,M≤105
所有输入数据在int范围内
这道题是用来连树链剖分的,具体的可以看看他人的博客,这里我只放上自己的代码,ac。。
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll x = 0 , f = 1; char ch = getchar(); for ( ; !isdigit(ch) ; ch = getchar()) if (ch == '-') f = -1; for ( ; isdigit(ch) ; ch = getchar()) x = x * 10 + ch - '0'; return x * f; } const ll maxn = 1e6 + 10; const ll maxEdge = 2e6 + 10; struct SegTree { ll leftSon , rightSon , weight , LazyTagAdd; }segTree[maxn << 2]; struct Edge { ll u , v , next; }edge[maxEdge]; ll n , m , r , cnt = 0 , a[maxn] , b[maxn]; ll num = 1 , head[maxn] , _mod; ll deep[maxn] , fa[maxn] , son[maxn] , tot[maxn] , top[maxn] , idx[maxn]; inline void add_edge(ll from , ll to) { edge[num].u = from; edge[num].v = to; edge[num].next = head[from]; head[from] = num ++; } inline void pushUp(ll root) { segTree[root].weight = (segTree[root << 1].weight + segTree[root << 1 | 1].weight) % _mod; } inline void pushDown(ll root) { segTree[root << 1].LazyTagAdd += segTree[root].LazyTagAdd; segTree[root << 1].LazyTagAdd %= _mod; segTree[root << 1 | 1].LazyTagAdd += segTree[root].LazyTagAdd; segTree[root << 1 | 1].LazyTagAdd %= _mod; segTree[root << 1].weight += segTree[root].LazyTagAdd * (segTree[root << 1].rightSon - segTree[root << 1].leftSon + 1); segTree[root << 1].weight %= _mod; segTree[root << 1 | 1].weight += segTree[root].LazyTagAdd * (segTree[root << 1 | 1].rightSon - segTree[root << 1 | 1].leftSon + 1); segTree[root << 1 | 1].weight %= _mod; segTree[root].LazyTagAdd = 0; return ; } void build(ll root , ll _left , ll _right) { segTree[root].leftSon = _left , segTree[root].rightSon = _right , segTree[root].LazyTagAdd = 0; if (_left == _right) { segTree[root].weight = a[_left]; return ; } ll _mid = (_left + _right) >> 1; build(root << 1 , _left , _mid); build(root << 1 | 1 , _mid + 1 , _right); pushUp(root); } ll querySum(ll root , ll _left , ll _right) { if (segTree[root].rightSon < _left || _right < segTree[root].leftSon) { return 0; } //cout << root << " " << segTree[root].leftSon << " " << segTree[root].rightSon << " " << segTree[root].weight << endl; if (_left <= segTree[root].leftSon && segTree[root].rightSon <= _right) { return segTree[root].weight % _mod; } if (segTree[root].LazyTagAdd) { pushDown(root); } ll _mid = ((segTree[root].leftSon + segTree[root].rightSon) >> 1) , ans = 0; if (_left <= _mid) { ans += querySum(root << 1 , _left , _right); ans %= _mod; } if (_mid < _right) { ans += querySum(root << 1 | 1 , _left , _right); ans %= _mod; } return ans; } void addNum(ll root , ll _left , ll _right , ll add) { if (segTree[root].rightSon < _left || _right < segTree[root].leftSon) { return ; } if (_left <= segTree[root].leftSon && segTree[root].rightSon <= _right) { segTree[root].weight += (segTree[root].rightSon - segTree[root].leftSon + 1) * add; segTree[root].weight %= _mod; segTree[root].LazyTagAdd += add; segTree[root].LazyTagAdd %= _mod; return ; } if (segTree[root].LazyTagAdd) { pushDown(root); } ll _mid = (segTree[root].leftSon + segTree[root].rightSon) >> 1; if (_left <= _mid) { addNum(root << 1 , _left , _right , add); } if (_mid < _right) { addNum(root << 1 | 1 , _left , _right , add); } pushUp(root); return ; } ll dfs1(ll now , ll f , ll dep) { deep[now] = dep; fa[now] = f; tot[now] = 1; ll maxSon = -1; for (ll e = head[now] ; e != -1 ; e = edge[e].next) { if (edge[e].v == f) { continue; } tot[now] += dfs1(edge[e].v , now , dep + 1); if (tot[edge[e].v] > maxSon) { maxSon = tot[edge[e].v]; son[now] = edge[e].v; } } return tot[now]; } void dfs2(ll now , ll topf) { idx[now] = ++ cnt; a[cnt] = b[now]; top[now] = topf; if (!son[now]) { return ; } dfs2(son[now] , topf); for (ll e = head[now] ; e != -1 ; e = edge[e].next) { if (!idx[edge[e].v]) { dfs2(edge[e].v , edge[e].v); } } } void treeSum(ll x , ll y) { ll ans = 0; while (top[x] != top[y]) { if (deep[top[x]] < deep[top[y]]) { swap(x , y); } ans = (ans + querySum(1 , idx[top[x]] , idx[x])) % _mod; x = fa[top[x]]; } if (deep[x] > deep[y]) { swap(x , y); } ans = (ans + querySum(1 , idx[x] , idx[y])) % _mod; printf("%lld\n" , ans % _mod); return ; } void treeAdd(ll x , ll y , ll val) { while (top[x] != top[y]) { if (deep[top[x]] < deep[top[y]]) { swap(x , y); } addNum(1 , idx[top[x]] , idx[x] , val); x = fa[top[x]]; } if (deep[x] > deep[y]) { swap(x , y); } addNum(1 , idx[x] , idx[y] , val); } int main() { memset(head , -1 , sizeof(head)); n = read() , m = read() , r = read() , _mod = read(); for (ll i = 1 ; i <= n ; i ++) { b[i] = read(); } for (ll i = 1 ; i < n ; i ++) { ll x = read() , y = read(); add_edge(x , y) , add_edge(y , x); } dfs1(r , 0 , 1); dfs2(r , r); build(1 , 1 , n); while (m --) { ll opr = read(); if (opr == 1) { ll x = read() , y = read() , z = read(); z %= _mod; treeAdd(x , y , z); } else if (opr == 2) { ll x = read() , y = read(); treeSum(x , y); } else if (opr == 3) { ll x = read() , z = read(); addNum(1 , idx[x] , idx[x] + tot[x] - 1 , z % _mod); } else if (opr == 4) { ll x = read(); printf("%lld\n" , querySum(1 , idx[x] , idx[x] + tot[x] - 1)); } } }