Q:什么是树链剖分?
A:在一棵树上,划分轻重链来维护树上的区间(语言表达能力极差所以我决定复制一段过来
在一棵树上进行路径的修改、求极值、求和”乍一看只要线段树就能轻松解决,实际上,仅凭线段树是不能搞定它的。我们需要用到一种貌似高级的复杂算法——树链剖分。树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。
树链剖分有几个操作,dfs1预处理出每个节点的size(质量,以i为根的节点有多少孩子节点),hson(最重的一个孩子节点),fa父亲节点和deep深度。dfs2划分轻重链,先遍历重儿子,记录top(链头元素),把树中节点映射到线段树里(赋一个递增值到id数组然后记录下这个值真实对应的点)。然后再遍历其他轻儿子(轻儿子的链头元素为他自己)。
(讲的还是好乱啊。。
然后用数据结构来维护(下面代码是线段树。)
然后怎么对树上元素操作呢?我们发现,在同一个链上的元素的虚拟的序号(就是你之前映射到线段树里的那个)是连续的。所以同一个链上的两点查询修改就是非常容易,直接对那个id区间修改就好了。记得dep小的是区间左端点。
那么怎么对不同链上的元素操作呢。我们可以让这两个链头往上跳(跳到上一个链的链头。)边跳边修改,直到跳到一个同一个链上。
——————以上的乱乱的思路。
以下是丑丑的代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int head[MAXN],cnt=0,n,m,r,p;
struct edge{
int to,next;
}e[MAXN<<1];
inline void add(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}
int fa[MAXN],hson[MAXN],size[MAXN],dep[MAXN];
void dfs1(int u,int father){
fa[u]=father;
size[u]=1;
dep[u]=dep[father]+1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==father)continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[hson[u]]||!hson[u])hson[u]=v;
}
}
int top[MAXN],id[MAXN],real1[MAXN],num=0;
void dfs2(int u,int tp){
top[u]=tp;
id[u]=++num;
real1[num]=u;
if(hson[u])dfs2(hson[u],tp);
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa[u]||v==hson[u])continue;
dfs2(v,v);
}
}
#define mid ((l+r)>>1)
#define lson (o<<1)
#define rson (o<<1|1)
int sumv[MAXN<<2],addv[MAXN<<2],a[MAXN];
inline void pushup(int o){sumv[o]=(sumv[lson]+sumv[rson])%p;}
inline void pushdown(int o,int l,int r){
if(addv[o]){
(addv[lson]+=addv[o])%=p;(addv[rson]+=addv[o])%=p;
(sumv[lson]+=addv[o]*(mid-l+1))%=p;(sumv[rson]+=addv[o]*(r-mid))%=p;
addv[o]=0;
}
}
inline void bulidt(int o,int l,int r){
addv[o]=0;
if(l==r){sumv[o]=a[real1[l]];return;}
bulidt(lson,l,mid);bulidt(rson,mid+1,r);
pushup(o);
}
inline long long query(int o,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r)return sumv[o];
long long ans=0;
pushdown(o,l,r);
if(ql<=mid)(ans+=query(lson,l,mid,ql,qr))%=p;
if(qr>mid)(ans+=query(rson,mid+1,r,ql,qr))%=p;
return ans;
}
inline void change(int o,int l,int r,int ql,int qr,long long ww){
if(ql<=l&&qr>=r){(sumv[o]+=(r-l+1)*ww)%=p;(addv[o]+=ww)%=p;return;}
pushdown(o,l,r);
if(ql<=mid)change(lson,l,mid,ql,qr,ww);
if(qr>mid)change(rson,mid+1,r,ql,qr,ww);
pushup(o);
}
inline void chain_add(int u,int v,long long ww){
int fx=top[u],fy=top[v];
while(fx!=fy){
if(dep[fx]>dep[fy]){
change(1,1,n,id[fx],id[u],ww);
u=fa[fx];
}
else {
change(1,1,n,id[fy],id[v],ww);
v=fa[fy];
}
fx=top[u],fy=top[v];
}
if(dep[u]<dep[v])swap(u,v);
change(1,1,n,id[v],id[u],ww);
}
inline long long chain_chazhao(int u,int v){
int fx=top[u],fy=top[v];
long long ans=0;
while(fx!=fy){
if(dep[fx]>dep[fy]){
(ans+=query(1,1,n,id[fx],id[u]))%=p;
u=fa[fx];
}
else {
(ans+=query(1,1,n,id[fy],id[v]))%=p;
v=fa[fy];
}
fx=top[u],fy=top[v];
}
if(dep[u]<dep[v])swap(u,v);
(ans+=query(1,1,n,id[v],id[u]))%=p;
return ans%p;
}
inline long long zishu(int u){return query(1,1,n,id[u],id[u]+size[u]-1);}
inline long long gaizishu(int u,long long ww){
change(1,1,n,id[u],id[u]+size[u]-1,ww);
}
int main(){
scanf("%d%d%d%d",&n,&m,&r,&p);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++){
int tem1,tem2;
scanf("%d%d",&tem1,&tem2);
add(tem1,tem2);
add(tem2,tem1);
}
dfs1(r,r);
dfs2(r,r);
bulidt(1,1,n);
for(int i=1;i<=m;i++){
int leixing,zuo,you,val;
scanf("%d",&leixing);
if(leixing==1){
scanf("%d%d%d",&zuo,&you,&val);
chain_add(zuo,you,val);
}
if(leixing==2){
scanf("%d%d",&zuo,&you);
printf("%lld\n",chain_chazhao(zuo,you));
}
if(leixing==3){
scanf("%d%d",&zuo,&val);
gaizishu(zuo,val);
}
if(leixing==4){
scanf("%d",&zuo);
printf("%lld\n",zishu(zuo));
}
}
return 0;
}