树链剖分
树链剖分
没有好好改题,来学数据结构(我没脸)。
基本概念:
重儿子:子树结点数目最多的结点。
轻儿子:父亲节点中除了重儿子以外的结点。
重边:父亲结点和重结点连成的边。
轻边:父亲节点和轻节点连成的边。
重链:由多条重边连接而成的路径。
轻链:由多条轻边连接而成的路径。
数组定义:
$siz[u]$:以$u$为根的子树节点个数
$top[u]$:当前节点所在重链的顶端节点
$son[u]$:$u$的重儿子
$dep[u]$:结点$u$的深度
$fa[u]\ $:结点$u$的父亲节点
$tid[u]$:树中每个节点剖分以后的新编号($DFS$的执行顺序)
$rnk[u]$:$DFS$序中节点在树中的位置
预处理:
两遍$dfs$。
第一遍$dfs$,处理出每个点的$dep[u],fa[u],son[u],siz[u]$。
第二遍$dfs$,处理出每个点的$top[u],tid[u],rnk[u]$,即将各个重结点连接成重链,轻节点连接成轻链,并且将重链(其实就是一段区间)用数据结构(一般是树状数组或线段树)来进行维护,并且为每个节点进行编号。
应用:
1.单点修改:按新编号在线段树上修改。
2.路径修改(查询):
(1).修改点x到点y路径上各点的值
需要一个神奇的跳链操作。
具体操作为:
$u$和$v$在同一条重链上时,直接线段树区间修改即可。
$u$和$v$不在同一条重链上时,我们要想办法把它们往同一条重链上靠。
$1$.$fa[top[u]]$与$v$在同一条重链上,做两次区间修改即可。
$2$.$u$经过若干条重链与轻边后和$v$在同一条重链上,多次做区间修改即可。
$3$.$u$和$v$都经过若干条重链与轻边后在同一条重链上,那么每次找到深度较深的点$x$,做一次区间修改后,再将该点跳到$fa[top[x]]$,直到$u$和$v$在同一条重链上
(2).查询点x到点y路径上各点的值
同上。
(3).修改点x子树上各点的值
考虑到子树内dfs序是相连的。
所以被修改区间是一个连续的区间,所以直接在线段树上修改区间$[tid[i],tid[i]+siz[i]-1]$即可。
(4).查询点x子树上各点的值
同上。
模板:
颓的代码。。。
#include <bits/stdc++.h>
#define ll long long
#define N 100005
using namespace std;
inline ll read()
{
register ll x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register ll x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[36];int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct node{
int to,next;
}e[N<<1];
int head[N],cnt=0;
inline void add(register int u,register int v)
{
e[++cnt]=(node){v,head[u]};
head[u]=cnt;
}
ll ch[N];
ll n,m,rt,mod;
ll size[N],dep[N],fa[N],son[N];
ll tot=0,dl[N],a[N],top[N];
inline void dfs1(register int x)
{
size[x]=1;
for(register int i=head[x];i;i=e[i].next)
if(e[i].to!=fa[x])
{
dep[e[i].to]=dep[x]+1;
fa[e[i].to]=x;
dfs1(e[i].to);
size[x]+=size[e[i].to];
if(size[e[i].to]>size[son[x]])
son[x]=e[i].to;
}
}
inline void dfs2(register int x,register int t)
{
dl[x]=++tot;
a[tot]=ch[x];
top[x]=t;
if(son[x])
dfs2(son[x],t);
for(register int i=head[x];i;i=e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=son[x])
dfs2(e[i].to,e[i].to);
}
ll sum[N<<3],tag[N<<3];
inline void pushup(register int x)
{
sum[x]=sum[x<<1]+sum[x<<1|1];
sum[x]%=mod;
}
inline void build(register int x,register int l,register int r)
{
if(l==r)
{
sum[x]=a[l];
tag[x]=0;
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
}
inline void pushdown(register int x,register int l,register int r)
{
int ls=x<<1,rs=x<<1|1,mid=l+r>>1;
sum[ls]+=(mid-l+1)*tag[x];
sum[rs]+=(r-mid)*tag[x];
tag[ls]+=tag[x];
tag[rs]+=tag[x];
sum[ls]%=mod;
sum[rs]%=mod;
tag[ls]%=mod;
tag[rs]%=mod;
tag[x]=0;
}
inline void update(register int x,register int l,register int r,register int L,register int R,register int k)
{
if(L<=l&&r<=R)
{
sum[x]+=(r-l+1)*k;
tag[x]+=k;
sum[x]%=mod;
tag[x]%=mod;
return;
}
if(tag[x])
pushdown(x,l,r);
int mid=l+r>>1;
if(L<=mid)
update(x<<1,l,mid,L,R,k);
if(R>=mid+1)
update(x<<1|1,mid+1,r,L,R,k);
pushup(x);
}
inline ll query(register int x,register int l,register int r,register int L,register int R)
{
if(L<=l&&r<=R)
return sum[x];
if(tag[x])
pushdown(x,l,r);
ll res=0;
int mid=l+r>>1;
if(L<=mid)
res+=query(x<<1,l,mid,L,R)%mod;
if(R>=mid+1)
res+=query(x<<1|1,mid+1,r,L,R)%mod;
return res%mod;
}
inline void cal1(register int x,register int y,register int z)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy])
{
swap(x,y);
swap(fx,fy);
}
update(1,1,tot,dl[fx],dl[x],z);
x=fa[fx];
fx=top[x];
}
if(dl[x]>dl[y])
swap(x,y);
update(1,1,tot,dl[x],dl[y],z);
}
inline ll cal2(register int x,register int y)
{
ll res=0;
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy])
{
swap(x,y);
swap(fx,fy);
}
res=(res%mod+query(1,1,tot,dl[fx],dl[x])%mod)%mod;
x=fa[fx];
fx=top[x];
}
if(dl[x]>dl[y])
swap(x,y);
res=(res%mod+query(1,1,tot,dl[x],dl[y])%mod)%mod;
return res%mod;
}
int main()
{
n=read(),m=read(),rt=read(),mod=read();
for(register int i=1;i<=n;++i)
ch[i]=read(),ch[i]%=mod;
for(register int i=1;i<n;++i)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
dep[rt]=1;
fa[rt]=rt;
dfs1(rt);
dfs2(rt,rt);
build(1,1,n);
while(m--)
{
int opt=read();
if(opt==1)
{
int x=read(),y=read(),z=read();
cal1(x,y,z%mod);
}
else if(opt==2)
{
int x=read(),y=read();
write(cal2(x,y)%mod);
printf("\n");
}
else if(opt==3)
{
int x=read(),z=read();
update(1,1,tot,dl[x],dl[x]+size[x]-1,z%mod);
}
else
{
int x=read();
write(query(1,1,tot,dl[x],dl[x]+size[x]-1)%mod);
printf("\n");
}
}
return 0;
}
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);
putchar(x%10+'0');
}
const int N=1e5;
int v[N+10],dfn[N+10],Line[N+10];
int n,m,root,mod;
struct Segment{//线段树Lazy标记操作
#define ls (p<<1)
#define rs ((p<<1)|1)
int tree[(N<<2)+10],Lazy[(N<<2)+10];
void updata(int p){tree[p]=(tree[ls]+tree[rs])%mod;}
void add_tag(int p,int l,int r,int v){
tree[p]=(tree[p]+1ll*(r-l+1)*v)%mod;
Lazy[p]=(Lazy[p]+v)%mod;
}
void pushdown(int p,int l,int r){
if (!Lazy[p]) return;
int mid=(l+r)>>1;
add_tag(ls,l,mid,Lazy[p]),add_tag(rs,mid+1,r,Lazy[p]);
Lazy[p]=0;
}
void build(int p,int l,int r){
if (l==r){tree[p]=v[Line[l]]%mod;return;}
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
updata(p);
}
void insert(int p,int l,int r,int x,int y,int v){
if (x<=l&&r<=y){add_tag(p,l,r,v);return;}
int mid=(l+r)>>1;
pushdown(p,l,r);
if (x<=mid) insert(ls,l,mid,x,y,v);
if (y>mid) insert(rs,mid+1,r,x,y,v);
updata(p);
}
int query(int p,int l,int r,int x,int y){
if (x<=l&&r<=y) return tree[p];
pushdown(p,l,r);
int mid=(l+r)>>1,res=0;
if (x<=mid) res=(res+query(ls,l,mid,x,y))%mod;
if (y>mid) res=(res+query(rs,mid+1,r,x,y))%mod;
return res;
}
}Tree;
struct Start{
int pre[(N<<1)+10],child[(N<<1)+10],now[N+10];
int deep[N+10],size[N+10],fa[N+10],Rem[N+10],top[N+10];
int tot,cnt;
void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
void build(int x,int Deep){//第一遍dfs
deep[x]=Deep,size[x]=1;
for (int p=now[x],son=child[p],Max=0;p;p=pre[p],son=child[p]){
if (son==fa[x]) continue;
fa[son]=x;
build(son,Deep+1);
size[x]+=size[son];
if (Max<size[son]) Max=size[son],Rem[x]=son;
}
}
void dfs(int x){//第二遍dfs
if (!x) return;
Rem[fa[x]]==x?top[x]=top[fa[x]]:top[x]=x;
dfn[x]=++cnt,Line[cnt]=x;
dfs(Rem[x]);
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa[x]||son==Rem[x]) continue;
dfs(son);
}
}
void insert(int x,int y,int z){//区间操作
while (top[x]!=top[y]){
if (deep[top[x]]<deep[top[y]]) swap(x,y);
Tree.insert(1,1,n,dfn[top[x]],dfn[x],z);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
Tree.insert(1,1,n,dfn[x],dfn[y],z);
}
int query(int x,int y){//区间查询
int res=0;
while (top[x]!=top[y]){
if (deep[top[x]]<deep[top[y]]) swap(x,y);
res=(res+Tree.query(1,1,n,dfn[top[x]],dfn[x]))%mod;
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
res=(res+Tree.query(1,1,n,dfn[x],dfn[y]))%mod;
return res;
}
}T;
int main(){
n=read(),m=read(),root=read(),mod=read();
for (int i=1;i<=n;i++) v[i]=read();
for (int i=1,x,y;i<n;i++) x=read(),y=read(),T.join(x,y),T.join(y,x);
T.build(root,1),T.dfs(root),Tree.build(1,1,n);
for (int i=1;i<=m;i++){
int t=read();
if (t==1){
int x=read(),y=read(),z=read();
T.insert(x,y,z);
}
if (t==2){
int x=read(),y=read();
printf("%d\n",T.query(x,y));
}
if (t==3){//子树修改
int x=read(),z=read();
Tree.insert(1,1,n,dfn[x],dfn[x]+T.size[x]-1,z);
}
if (t==4){//子树查询
int x=read();
printf("%d\n",Tree.query(1,1,n,dfn[x],dfn[x]+T.size[x]-1));
}
}
return 0;
}

浙公网安备 33010602011771号