P3384 【模板】树链剖分
作者:互联网
大佬博客:https://www.cnblogs.com/chinhhh/p/7965433.html#firstt
题目描述
如题,已知一棵包含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取模)
输入输出样例
说明/提示
时空限制:1s,128M
数据规模:
对于30%的数据: N≤10,M≤10N≤10,M≤10
对于70%的数据: N≤103,M≤103N≤103,M≤103
对于100%的数据: N≤105,M≤105N≤105,M≤105
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<stack> #include<cstdlib> #include<queue> #include<set> #include<string.h> #include<vector> #include<deque> #include<map> using namespace std; #define INF 0x3f3f3f3f3f3f3f3f #define inf 0x3f3f3f3f #define eps 1e-6 #define bug printf("*********\n") #define debug(x) cout<<#x"=["<<x<<"]" <<endl typedef long long LL; typedef long long ll; const int maxn = 2e5 + 50; //const LL mod = 1e9 + 7; int n,m,r,mod; int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn]; //链式前向性数组,w[],wt[]初始点权数组 int st[maxn << 2],lazy[maxn << 2]; //线段树数组,lazy操作 int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn]; //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点 int res = 0; //答案 inline void add(int x,int y) { //链式前向性加边 to[++e] = y; nex[e] = beg[x]; beg[x] = e; } //------------------------------------------------------------------------------------------以下为线段树 inline void pushdown(int o,int l,int r) { if(lazy[o]) { lazy[o << 1] += lazy[o]; lazy[o << 1 | 1] += lazy[o]; int m = (l + r) >> 1; st[o << 1] += lazy[o] * (m - l + 1); st[o << 1 | 1] += lazy[o] * (r - m); lazy[o] = 0; } } inline void build(int o,int l,int r) { if(l == r) { st[o] = wt[l] % mod; return; } int mid = (l + r) >> 1; build(o << 1, l, mid); build(o << 1 | 1,mid + 1, r); st[o] = (st[o << 1] + st[o << 1 | 1]) % mod; } inline void query(int o,int l,int r,int ql,int qr) { if(ql <= l && r <= qr) { res += st[o]; res %= mod; return; } else { if(lazy[o]) pushdown(o,l,r); int mid = (l + r) >> 1; if(ql <= mid) query(o << 1, l, mid, ql ,qr); if(qr > mid) query(o << 1 | 1,mid + 1, r,ql,qr); } } inline void update(int o,int l,int r,int ql,int qr,int k) { if(ql <= l && r <= qr) { lazy[o] += k; st[o] += k * (r - l + 1); } else { if(lazy[o]) pushdown(o,l,r); int mid = (l + r) >> 1; if(ql <= mid) update(o << 1, l, mid, ql, qr,k); if(qr > mid) update(o << 1 | 1, mid + 1, r, ql, qr, k); st[o] = (st[o << 1] + st[o << 1 | 1]) % mod; } } //-----------------------------------------------------------------------------------以上为线段树 inline int qRange(int x,int y) { //x到y的链上的值的总和 int ans = 0; while(top[x] != top[y]) { //当两个点不在同一条链上 if(dep[top[x]] < dep[top[y]]) swap(x,y); //把x点改为所在链顶端的深度更深的那个点 res = 0; query(1, 1, n,id[top[x]],id[x]); //ans加上x点到x所在链端这一段区间的点权和 ans += res; ans %= mod; x = fa[top[x]]; //把x跳到x所在链顶端的那个点的上面一个点 } //直到两个点处于一点链上 if(dep[x] > dep[y]) swap(x,y); //把x点深度更深的那个点 res = 0; query(1, 1, n, id[x], id[y]); //这时再加上此时两个点的区间和即可 ans += res; return ans % mod; } inline void updRange(int x,int y,int k) { //x 到 y链上的值加k k %= mod; while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x,y); update(1, 1, n, id[top[x]], id[x], k); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x,y); update(1, 1, n, id[x], id[y], k); } inline int qSon(int x) { //以x为根节点的子树内所有节点的值之和 res = 0; query(1, 1, n, id[x], id[x] + siz[x] - 1); return res; } inline void updSon(int x,int k) { //以x为根节点的子树都加k update(1, 1, n, id[x], id[x] + siz[x] - 1, k); } inline void dfs1(int x,int f,int deep) { //x当前节点 f父亲 deep深度 dep[x] = deep; //标记每个点的深度 fa[x] = f; //标记每个点的父亲 siz[x] = 1; //标记每个非叶子节点的子树大小 int maxson = -1; //记录重儿子的儿子数 for(int i = beg[x]; i; i = nex[i]) { int y = to[i]; if(y == f) continue; dfs1(y,x,deep + 1); //dfs儿子节点 siz[x] += siz[y]; //把它的儿子数加到它身上 if(siz[y] > maxson) { //标记每个非叶子节点的重儿子编号 son[x] = y; maxson = siz[y]; } } } inline void dfs2(int x,int topf) { //x当前节点 topf当前链的最顶端的节点 id[x] = ++ cnt; //标记每个点的新编号 wt[cnt] = w[x]; //把每个点的初始值赋到新编号上来 top[x] = topf; //这个点所在链的顶端 if(!son[x]) return; //如果没有儿子则返回 dfs2(son[x],topf); //按先处理重儿子,再处理轻儿子的顺序递归处理 for(int i = beg[x]; i; i = nex[i]) { int y = to[i]; if(y == fa[x] || y == son[x]) continue; dfs2(y,y); //对于每一个轻儿子都有一条从它自己开始的链 } } int main() { scanf("%d %d %d %d", &n, &m, &r, &mod); for (int i = 1; i <= n; i++) scanf("%d", &w[i]); for (int i = 1; i < n; i++) { int a, b; scanf("%d %d", &a, &b); add(a, b); add(b, a); } dfs1(r, 0, 1); dfs2(r, r); build(1, 1, n); while(m--) { int k,x,y,z; scanf("%d",&k); if(k == 1) { scanf("%d %d %d",&x,&y,&z); updRange(x,y,z); } else if(k == 2) { scanf("%d %d",&x,&y); printf("%d\n",qRange(x,y)); } else if(k == 3) { scanf("%d %d",&x,&y); updSon(x,y); }else { scanf("%d",&x); printf("%d\n",qSon(x)); } } }View Code
标签:剖分,int,siz,top,树链,P3384,id,include,节点 来源: https://www.cnblogs.com/smallhester/p/11599766.html