1、我的脑子要被剖开啦。
2、辣鸡教材!
3、抱紧大佬博客才是最好的!
当然了,思想是在教材上看的,但是教材上一条重链一个线段树真的是……emmmm
然后就参考了正确的入门姿势,成功过了洛谷上的模板题。
树链剖分,就是将树拆成一小段一小段连续的链,得到线性的数据,但这个线性的数据使用是有限制的,比如跨段查询或修改的时候就会出错,那就只能一段一段处理,然后再合并。这时要尽量使每段更长些,那么就可以将任务尽可能多的交给线段树处理,这样复杂度是log级别的。
通过两次dfs将整颗树分成轻链和重链(同一重链在线段树上是连续的),这样将树拆成一段一段连续的链然后放到线段树上去,当查询两个节点之间的东西时,通过top[]数组和fa[]数组将这两个节点向上挪动,每次将deep[]较深的b移到fa[top[b]]处(此过程中顺便处理top[b]->b在线段树上值),重复此过程直到两个节点的top[]相同,top[]相同就意味着此时这两个节点在同一重链上,此时再查找一次(同一重链在线段树上是连续的)。
处理以节点x为根的整个子树:用线段树处理[pos[x],pos[x]+siz[x]-1]即可
查询节点x和节点y的LCA(最近公共祖先):将两点不断向上挪动并处理,直到两点的top[]相同,deep[]小的点即是最近公共祖先
处理节点x到节点y的路径上的点:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[x],pos[y]]即可
处理节点x到节点y的路径上的边:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[son[x]],pos[y]]即可(用每个点表示通向其父节点的边)
以洛谷模板题为例给出代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100010;
struct line{
int y,next;
}l[MAXN<<1];
int n,m,r,p,Left,Right,x,y,z,type,cnt=0;
int fa[MAXN],deep[MAXN],son[MAXN],siz[MAXN],fi[MAXN];
int top[MAXN],pos[MAXN],num=0,in[MAXN],temp[MAXN];
int t[MAXN<<2],delta[MAXN<<2];
int read(){
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
return num;
}
void make_line(int x,int y){
l[++cnt].y=y;l[cnt].next=fi[x];fi[x]=cnt;
l[++cnt].y=x;l[cnt].next=fi[y];fi[y]=cnt;
}
void dfs(int x,int f,int d){//第一次dfs,寻找重儿子
fa[x]=f;deep[x]=d;
son[x]=0;siz[x]=1;
for(int i=fi[x];i;i=l[i].next){
int v=l[i].y;
if(v==f)continue;
dfs(v,x,d+1);
siz[x]+=siz[v];
if(siz[v]>siz[son[x]])son[x]=v;
}
}
void dfs2(int x,int tp){//第二次dfs,构造重链,记录节点在线段树上的位置
top[x]=tp;pos[x]=++num;
if(son[x])dfs2(son[x],tp);
for(int i=fi[x];i;i=l[i].next){
int v=l[i].y;
if(v==fa[x]||v==son[x])continue;
dfs2(v,v);
}
}
void make_tree(int l,int r,int root){//初始化
if(l==r){
t[root]=in[l];
return;
}
int mid=(l+r)>>1;
make_tree(l,mid,root<<1);
make_tree(mid+1,r,root<<1|1);
t[root]=t[root<<1]+t[root<<1|1];
}
void push_down(int l,int r,int root){//下移delta
int mid=(l+r)>>1;
delta[root<<1]+=delta[root];delta[root<<1|1]+=delta[root];
delta[root<<1]%=p;delta[root<<1|1]%=p;
t[root<<1]=(t[root<<1]+delta[root]*(mid-l+1))%p;
t[root<<1|1]=(t[root<<1|1]+delta[root]*(r-mid))%p;
delta[root]=0;
}
void add(int l,int r,int root){//线段树模板
if(r<Left||l>Right)return;
if(Left<=l&&Right>=r){
delta[root]+=z;
delta[root]%=p;
t[root]=(t[root]+z*(r-l+1))%p;
return;
}
int mid=(l+r)>>1;
push_down(l,r,root);
add(l,mid,root<<1);
add(mid+1,r,root<<1|1);
t[root]=t[root<<1]+t[root<<1|1];
return;
}
long long find(int l,int r,int root){//模板<<1
if(r<Left||l>Right)return 0;
if(Left<=l&&Right>=r)return t[root];
int mid=(l+r)>>1;
push_down(l,r,root);
return (find(l,mid,root<<1)+find(mid+1,r,root<<1|1))%p;
}
void change(){
int a=read(),b=read();z=read();
int ta=top[a],tb=top[b];
while(ta!=tb){
if(deep[ta]>deep[tb]){
swap(ta,tb);
swap(a,b);
}
Left=pos[tb];Right=pos[b];
add(1,n,1);//分段处理
b=fa[tb];tb=top[b];
}
if(deep[a]>deep[b])swap(a,b);
Left=pos[a];
Right=pos[b];
add(1,n,1);//同一重链后的处理
}
void query(){
int a=read(),b=read();
int ta=top[a],tb=top[b];
long long ans=0;
while(ta!=tb){
if(deep[ta]>deep[tb]){
swap(ta,tb);
swap(a,b);
}
Left=pos[tb];Right=pos[b];
ans+=find(1,n,1);//同上
ans%=p;
b=fa[tb];tb=top[b];
}
if(deep[a]>deep[b])swap(a,b);
Left=pos[a];
Right=pos[b];
ans+=find(1,n,1);
ans%=p;
printf("%lld\n",ans);
}
void work(){
n=read();m=read();r=read();p=read();
for(int i=1;i<=n;++i)
temp[i]=read();
for(int i=1;i<n;++i){
x=read();y=read();
make_line(x,y);
}
dfs(r,r,1);
dfs2(r,r);
for(int i=1;i<=n;++i)
in[pos[i]]=temp[i];
make_tree(1,n,1);
for(int i=1;i<=m;++i){
type=read();
if(type==1)change();
if(type==2)query();
if(type==3){
x=read(),z=read();
Left=pos[x];Right=Left+siz[x]-1;//注意子树的操作
add(1,n,1);
}
if(type==4){
x=read();
Left=pos[x];Right=Left+siz[x]-1;
printf("%lld\n",find(1,n,1));
}
}
}
int main(){
work();
return 0;
}
模板先到这里,还有很多题要做……