LCA 学习笔记

据我所知,实现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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值