据我所知,实现LCA有三种方法:基于二分搜索、基于RMQ、树链剖分
基于二分搜索其实就是对父子节点做一个2^k表,也就是倍增的思想,但是这个二分搜索的搜索方式有点与众不同
int lca(int u,int v)
{
if(depth[u]>depth[v]) swap(u,v);
for(int k=0;k<max_log_v;k++)
{
if((depth[v]-depth[u])>>k&1)
{
v=fa[k][v];
}
}
if(u==v) return u;
for(int k=max_log_v-1;k>=0;k--)
{
if(fa[k][u]!=fa[k][v])
u=fa[k][u],v=fa[k][v];
}
return fa[0][u];
}
下面的那个for循环是逆向的,正向的不行;
基于RMQ的:
可以发现,两个点的最近公共祖先就是dfs序中路径中的深度最小值,也就是离根节点最近,可以用用线段树或者树状数组之类的维护一下,没什么特别的
注意:回溯时候的dfs序也要计入其中的,当然,还要记录某点第一次出现的dfs序;
而且依照dfs序的原则,似乎使用中序遍历就能使左子树的时间戳比右子树的小,然后回溯时候就不用记录了。
基于树链剖分的:
其实也就是树链剖分的一个很小的衍生品而已,只要会了树链剖分其实很简单的。
贴一个树链剖分的板子。
#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
long long n,m,r,p;
vector<int> a[1000005];
long long lazy[1000005],shu[maxn*10],b[maxn];
long long num[maxn],zhi[maxn],gao[maxn],ba[maxn],size[maxn],use[maxn],son[maxn],ding[maxn],cnt=0;
void dfs(long long x,long long fa,long long sum)
{
// cout<<"dfs"<<endl;
// cout<<x<<endl;
ba[x]=fa;
gao[x]=sum;
size[x]=1;
long long t=a[x].size();
long long da=0;
for(int i=0;i<t;i++)
{
long long u=a[x][i];
// long long maxn=0;
if(u!=fa)
{
dfs(u,x,sum+1);
size[x]+=size[u];
if(size[u]>da) da=size[u],son[x]=u;
}
}
}
void ddfs(long long x,long long top)
{
// cout<<"ddfs"<<endl;
// cout<<x<<" "<<top<<endl;
use[x]=1;
num[x]=++cnt;
ding[x]=top;
b[cnt]=zhi[x];
long long t=a[x].size();
if(!son[x]) return;//叶节点了
ddfs(son[x],top);
for(int i=0;i<t;i++)
{
if(!use[a[x][i]]) ddfs(a[x][i],a[x][i]);
}
}
void geng(long long k)
{
shu[k]=(shu[k*2]+shu[k*2+1])%p;
// shu[k]=shu[k*2]+shu[k*2+1];
}
void build(long long l,long long r,long long k)
{
// cout<<"build"<<endl;
if(l==r)
{
shu[k]=b[l]%p;return;
}
long long mid=(l+r)>>1;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
geng(k);
}
void down(long long k,long long l,long long r)
{
if(lazy[k]==0) return;
long long mid=(l+r)>>1;
lazy[k*2]=(lazy[k*2]+lazy[k])%p;
lazy[k*2+1]=(lazy[k*2+1]+lazy[k])%p;
shu[k*2]=(shu[k*2]+lazy[k]*(mid-l+1))%p;
shu[k*2+1]=(shu[k*2+1]+lazy[k]*(r-mid))%p;
// lazy[k*2]+=lazy[k],lazy[k*2+1]+=lazy[k];
// shu[k*2]+=(mid-l+1)*lazy[k],shu[k*2+1]+=(r-mid)*lazy[k];
lazy[k]=0;
}
void jian(long long l,long long r,long long x,long long y,long long k,long long z)
{
// cout<<"k="<<k<<" "<<shu[k]<<endl;
down(k,l,r);
long long mid=(l+r)>>1;
if(l>y||r<x) return;
else if(l>=x&&r<=y) shu[k]=(shu[k]+(r-l+1)*z)%p,lazy[k]=(lazy[k]+z)%p;
else
{
jian(l,mid,x,y,k*2,z);
jian(mid+1,r,x,y,k*2+1,z);
geng(k);
}
// cout<<"k="<<k<<" "<<shu[k]<<endl;
}
long long ccha(long long l,long long r,long long x,long long y,long long k)
{
long long ans=0;
down(k,l,r);
if(l>y||r<x) return 0;
else if(l>=x&&r<=y) return shu[k];
else
{
long long mid=(l+r)>>1;
ans=(ans+ccha(l,mid,x,y,k*2))%p;
ans=(ans+ccha(mid+1,r,x,y,k*2+1))%p;
// geng(k);
return ans;
}
}
void add(long long s,long long t,long long val)
{
long long ans=0;
while(ding[s]!=ding[t])
{
if(gao[ding[s]]<gao[ding[t]]) swap(s,t);
jian(1,cnt,min(num[ding[s]],num[s]),max(num[ding[s]],num[s]),1,val);
// cout<<"num="<<num[ding[s]]<<' '<<ding[s]<<endl;
s=ba[ding[s]];
// else swap(s,t);
}
if(gao[s]>gao[t]) swap(s,t);
jian(1,cnt,num[s],num[t],1,val);
// cout<<num[s]<<" "<<num[t]<<endl;
}
long long cha(long long s,long long t)
{
long long ans=0;
while(ding[s]!=ding[t])
{
// cout<<"ding"<<" "<<ding[s]<<" "<<ding[t]<<endl;
if(gao[ding[s]]<gao[ding[t]]) swap(s,t);
// cout<<"num="<<num[ding[s]]<<" "<<num[s]<<endl;
ans=(ans+ccha(1,cnt,min(num[ding[s]],num[s]),max(num[ding[s]],num[s]),1))%p;
s=ba[ding[s]];
// else swap(s,t);
}
if(gao[s]>gao[t]) swap(s,t);
ans=(ans+ccha(1,cnt,num[s],num[t],1))%p;
// if(ans<0) cout<<"hhh"<<endl;
// cout<<"num="<<num[ding[s]]<<" "<<num[s]<<endl;
return ans;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
std::ios::sync_with_stdio(false);
cin>>n>>m>>r>>p;
for(int i=1;i<=n;i++) cin>>zhi[i];
for(int i=1;i<n;i++)
{
long long l,r;
cin>>l>>r;
a[l].push_back(r);
a[r].push_back(l);
// cout<<l<<" "<<r<<endl;
}
dfs(r,r,1);//将高度,父子关系表示出来,还有树的大小,标记轻重节点
ddfs(r,r);//搞轻重链分别标序号,并把链头弄出来
// cout<<cnt<<endl;
build(1,cnt,1);
// cout<<"hhh"<<endl;
for(int i=1;i<=m;i++)
{
long long t;
cin>>t;
// cout<<"t="<<t<<endl;
if(t==1)
{
long long x,y,z;
cin>>x>>y>>z;
add(x,y,z%p);
}
else if(t==2)
{
long long x,y;
cin>>x>>y;
long long ans=cha(x,y);
cout<<ans<<endl;
}
else if(t==3)
{
long long x,z;
cin>>x>>z;
// cout<<num[x]<<" "<<num[x]+size[x]-1<<" hhh"<<endl;
jian(1,cnt,num[x],num[x]+size[x]-1,1,z%p);
}
else
{
long long x;
cin>>x;
long long ans=ccha(1,cnt,num[x],num[x]+size[x]-1,1);
cout<<ans<<endl;
}
}
// for(int i=1;i<=1000000;i++) if(shu[i]<0) cout<<"hhh"<<endl;
}