树链剖分
- 修改点x到点y的路径上的个点权值
- 查询点x到点y的路径上节点权值之和
- 修改点x子树上各点的权值
- 查询点x子树上所有节点的权值之和
1. 点权值
#include<bits/stdc++.h>
using namespace std;
const int N=100000+10;
int n,m,r,mod;
//以下是链式前向星
struct Edge{int to, next;}edge[2*N];
int head[2*N], cnt;
void init(); //与前一小节“洛谷P3379树链剖分”的init()一样
void addedge(int u,int v); //与前一小节“洛谷P3379树链剖分”的addedge()一样
//以下是线段树
int ls(int x){ return x<<1; } //定位左儿子:x*2
int rs(int x){ return x<<1|1;} //定位右儿子:x*2 + 1
int w[N],w_new[N]; //w[]、w_new[]初始点权
int tree[N<<2], tag[N<<2]; //线段树数组、lazy-tag操作
void addtag(int p,int pl,int pr,int d){ //给结点p打tag标记,并更新tree
tag[p] += d; //打上tag标记
tree[p] += d*(pr-pl+1); tree[p] %= mod; //计算新的tree
}
void push_up(int p){ //从下往上传递区间值
tree[p] = tree[ls(p)] + tree[rs(p)]; tree[p] %= mod;
}
void push_down(int p,int pl, int pr){
if(tag[p]){
int mid = (pl+pr)>>1;
addtag(ls(p),pl,mid,tag[p]); //把tag标记传给左子树
addtag(rs(p),mid+1,pr,tag[p]); //把tag标记传给右子树
tag[p] = 0;
}
}
void build(int p,int pl,int pr){ //建线段树
tag[p] = 0;
if(pl==pr){
tree[p] = w_new[pl]; tree[p] %= mod;
return;
}
int mid = (pl+pr) >> 1;
build(ls(p),pl,mid);
build(rs(p),mid+1,pr);
push_up(p);
}
void update(int L,int R,int p,int pl,int pr,int d){
if(L<=pl && pr<=R){ addtag(p, pl, pr,d); return; }
push_down(p,pl,pr);
int mid = (pl+pr) >> 1;
if(L<=mid) update(L,R,ls(p),pl,mid,d);
if(R> mid) update(L,R,rs(p),mid+1,pr,d);
push_up(p);
}
int query(int L,int R,int p,int pl,int pr){
if(pl>=L && R >= pr) return tree[p] %= mod;
push_down(p,pl,pr);
int res =0;
int mid = (pl+pr) >> 1;
if(L<=mid) res += query(L,R,ls(p),pl,mid);
if(R> mid) res += query(L,R,rs(p),mid+1,pr);
return res;
}
//以下是树链剖分
int son[N],id[N],fa[N],deep[N],siz[N],top[N];
void dfs1(int x, int father); //与前一小节“洛谷P3379树链剖分”dfs1()一样
int num = 0;
void dfs2(int x,int topx){ //x当前结点,topx当前链的最顶端的结点
id[x] = ++num; //对每个结点新编号
w_new[num] = w[x]; //把每个点的初始值赋给新编号
top[x]=topx; //记录x的链头
if(!son[x]) return; //x是叶子,没有儿子,返回
dfs2(son[x],topx); //先dfs重儿子
for(int i=head[x];~i;i=edge[i].next){ //再dfs轻儿子
int y=edge[i].to;
if(y!=fa[x] && y!=son[x]) dfs2(y,y);//每个轻儿子都有一条从它自己开始的链
}
}
void update_range(int x,int y,int z){ //和求LCA(x, y)的过程差不多
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
update(id[top[x]],id[x],1,1,n,z); //修改一条重链的内部
x = fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
update(id[x],id[y],1,1,n,z); //修改一条重链的内部
}
int query_range(int x,int y){ //和求LCA(x,y)的过程差不多
int ans=0;
while(top[x]!=top[y]){ //持续往上跳,直到若x和y属于同一条重链
if(deep[top[x]]<deep[top[y]]) swap(x,y); //让x是链头更深的重链
ans += query(id[top[x]],id[x],1,1,n); //加上x到x的链头这一段区间
ans %= mod;
x = fa[top[x]]; //x穿过轻边,跳到上一条重链
}
if(deep[x]>deep[y]) swap(x,y);
//若LCA(x, y) = y,交换x,y,让x更浅,使得id[x] <= id[y]
ans += query(id[x],id[y],1,1,n); //再加上x, y的区间和
return ans % mod;
}
void update_tree(int x,int k){ update(id[x],id[x]+siz[x]-1,1,1,n,k); }
int query_tree(int x){ return query(id[x],id[x]+siz[x]-1,1,1,n) % mod; }
int main(){
init(); //链式前向星初始化
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 u,v; scanf("%d%d",&u,&v);
addedge(u,v); addedge(v,u);
}
dfs1(r,0);
dfs2(r,r);
build(1,1,n); //建线段树
while(m--){
int k,x,y,z; scanf("%d",&k);
switch(k){
case 1:scanf("%d%d%d",&x,&y,&z);update_range(x,y,z); break; //修改点x到点y的路径上的个点权值
case 2:scanf("%d%d",&x,&y); printf("%d\n",query_range(x,y));break; //查询点x到点y的路径上节点权值之和
case 3: scanf("%d%d",&x,&y); update_tree(x,y); break; //修改点x子树上各点的权值
case 4: scanf("%d",&x); printf("%d\n",query_tree(x)); break; //查询点x子树上所有节点的权值之和
}
}
}
2. 边权值
将上述边权转化为点权,每条边上的权值赋给这条边下层的节点(根节点为0)
1. 区间求和
区间求和,多算一个LCA点的值
2. 区间查询
区间查询,多算一个LCA点的值
同上
#include<bits/stdc++.h>
using namespace std;
const int N=100000+10;
int n,m,r,mod;
struct Edge{int to, next;}edge[2*N];
int head[2*N], cnt;
void init();
void addedge(int u,int v);
int ls(int x){ return x<<1; }
int rs(int x){ return x<<1|1;}
int w[N],w_new[N];
int tree[N<<2], tag[N<<2];
void addtag(int p,int pl,int pr,int d){
tag[p] += d;
tree[p] += d*(pr-pl+1); tree[p] %= mod;
}
void push_up(int p){
tree[p] = (tree[ls(p)] + tree[rs(p)]) % mod;
}
void push_down(int p,int pl, int pr){
if(tag[p]){
int mid = (pl+pr)>>1;
addtag(ls(p),pl,mid,tag[p]);
addtag(rs(p),mid+1,pr,tag[p]);
tag[p] = 0;
}
}
void build(int p,int pl,int pr){
tag[p] = 0;
if(pl==pr){
tree[p] = w_new[pl]; tree[p] %= mod;
return;
}
int mid = (pl+pr) >> 1;
build(ls(p),pl,mid);
build(rs(p),mid+1,pr);
push_up(p);
}
void update(int L,int R,int p,int pl,int pr,int d){
if(L<=pl && pr<=R){ addtag(p, pl, pr,d); return; }
push_down(p,pl,pr);
int mid = (pl+pr) >> 1;
if(L<=mid) update(L,R,ls(p),pl,mid,d);
if(R> mid) update(L,R,rs(p),mid+1,pr,d);
push_up(p);
}
int query(int L,int R,int p,int pl,int pr){
if(pl>=L && R >= pr) return tree[p] %= mod;
push_down(p,pl,pr);
int res =0;
int mid = (pl+pr) >> 1;
if(L<=mid) res += query(L,R,ls(p),pl,mid);
if(R> mid) res += query(L,R,rs(p),mid+1,pr);
return res % mod;
}
int son[N],id[N],fa[N],deep[N],siz[N],top[N];
void dfs1(int x, int father);
int num = 0;
void dfs2(int x,int topx){
id[x] = ++num;
w_new[num] = w[x];
top[x]=topx;
if(!son[x]) return;
dfs2(son[x],topx);
for(int i=head[x];~i;i=edge[i].next){
int y=edge[i].to;
if(y!=fa[x] && y!=son[x]) dfs2(y,y);
}
}
int lca(int x, int y) {
while(top[x] != top[y]) {
if(deep[top[x]] < deep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return deep[x] < deep[y] ? x : y;
}
void update_range(int x,int y,int z){
int LCA = lca(x, y);
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
if(top[x] != top[LCA])
update(id[top[x]],id[x],1,1,n,z);
else
update(id[LCA]+1,id[x],1,1,n,z); // avoid updating LCA
x = fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
if(x != LCA)
update(id[x],id[y],1,1,n,z); // update range except LCA
else
update(id[x]+1,id[y],1,1,n,z); // avoid updating LCA
}
int query_range(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ans += query(id[top[x]],id[x],1,1,n);
ans %= mod;
x = fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans += query(id[x],id[y],1,1,n);
return ans % mod;
}
void update_tree(int x,int k){ update(id[x],id[x]+siz[x]-1,1,1,n,k); }
int query_tree(int x){ return query(id[x],id[x]+siz[x]-1,1,1,n) % mod; }
int main(){
init();
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 u,v; scanf("%d%d",&u,&v);
addedge(u,v); addedge(v,u);
}
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
while(m--){
int k,x,y,z; scanf("%d",&k);
switch(k){
case 1:scanf("%d%d%d",&x,&y,&z);update_range(x,y,z); break;
case 2:scanf("%d%d",&x,&y); printf("%d\n",query_range(x,y));break;
case 3: scanf("%d%d",&x,&y); update_tree(x,y); break;
case 4: scanf("%d",&x); printf("%d\n",query_tree(x)); break;
}
}
}