树上两两点距离和

题目描述


其中 n < = 1 0 5 , m < = 1 0 4 n<=10^5,m<=10^4 n<=105,m<=104

题解

在这里插入图片描述
发现 S [ l , r ] S[l,r] S[l,r]上的点是上图中根须部分
考虑如何将左侧点找出来,右侧是类似的
首先将一个点(区间)的父亲设为紧挨着这个区间并且是最长的区间
然后就会发现要找的左侧点会是,这样新建的树的一条链

二次转化

对于树上一条链可以转化为括号序上的一段区间

转化后的题意

给出一棵树,求编号在一段区间内的两两距离和

解法1

简单结论 d i s ( a , b ) = d e p ( a ) + d e p ( b ) − 2 d e p ( l c a ( a , b ) ) dis(a,b)=dep(a)+dep(b)-2dep(lca(a,b)) dis(a,b)=dep(a)+dep(b)2dep(lca(a,b))
根据简单结论就可以转化为求两两l l c a lca lca的深度和
对于序列先分块,枚举每一个块,然后用 O ( n ) O(n) O(n)时间处理处对于每个点与这些块内点的 l c a lca lca深度和
这样就可以处理出整块与整块和散块与整块之间的值
对于散块与散块之间的值,就 O ( n ) O(n) O(n)建虚树,在虚树上算(但是这样常数会很大,不知道有没有更简单的方法)
时间复杂度 O ( n 1.5 ) O(n^{1.5}) O(n1.5)

小优化

如果散块中的点个数大于这个块中的总点数的 1 2 \frac{1}{2} 21,那么就可以先加上这个块,然后再减去剩下的点

解法2

可以不用将树上路径转化为欧拉序上区间
对于树可以树上链分块,具体操作就是对于深度为阈值的倍数且子树大小大于等于阈值的点设为关键点
对于每个关键点到最近祖先关键点的点形成一块,按照和序列分块一样的方法做,只不过就比较难用到序列上的小优化了

code

#include<bits/stdc++.h>
#define fo(i,a,b)for(int i=a,_e=b;i<=_e;++i)
#define fd(i,a,b)for(int i=b,_e=a;i>=_e;--i)
#define ll long long
#define pb push_back
using namespace std;
const int M=4e4+100,G=100;
int n,nn,q,rt,ts,a,b,c,d,A,B,C,D;
int a0,a1,b0,b1,c0,c1,d0,d1;
int le[M],ri[M],s[M][2],dy[M];
int ld[M],de[M],fa[M],hv[M];
int L[M],R[M],su[M];
int dds;
ll ans;
struct nod{int x,c,q;}dd[M],ee[M];
namespace graph{
	int fa[M],de[M],en[M];
	int es,fi[M],lg[M],lc[17][M],K;
	int dl[M],ds,x;
	int T[256],T2[256],s[M],ss,sfa[M],sd[M],sds;
	int cc[M],qq[M];
	vector<int>e[M];
	void link(int x,int y){e[x].pb(y);e[y].pb(x);}
	#define cmin(a,b)(de[a]<de[b]?a:b)
	int lca(int x,int y){
		x=fi[x];y=fi[y];
		if(x>y)swap(x,y);
		K=lg[y-x];
		return cmin(lc[K][x],lc[K][y-(1<<K)+1]);
	}
	void dfs(int x){
		dl[++ds]=x;
		de[x]=de[fa[x]]+1;lc[0][++es]=x;fi[x]=es;
		fo(i,0,e[x].size()-1)if(e[x][i]!=fa[x])
			fa[e[x][i]]=x,dfs(e[x][i]),lc[0][++es]=x;
		en[x]=es;
	}
	void init(){
		dfs(1);
		fo(i,2,es)lg[i]=lg[i>>1]+1;
		fo(i,1,lg[es]){
			int nx=1<<i-1;
			fo(j,1,es-nx+1)lc[i][j]=cmin(lc[i-1][j],lc[i-1][j+nx]);
		}
	}
	void pre(){
		fo(i,1,4)su[nn+i]=0;
		fd(i,2,ds)x=dl[i],su[fa[x]]+=su[x];
		fo(i,2,ds)x=dl[i],su[x]+=su[fa[x]];
	}
	bool cmp(nod x,nod y){return fi[x.x]<fi[y.x];}
	void get_tree(){
		if(dds<32){
			stable_sort(dd+1,dd+dds+1,cmp);
		}else{
			memset(T,0,256*4);
			memset(T2,0,256*4);
			fo(i,1,dds)x=fi[dd[i].x],++T[x&255],++T2[x>>8];
			fo(i,1,255)T[i]+=T[i-1],T2[i]+=T2[i-1];
			fd(i,1,dds)ee[T[fi[dd[i].x]&255]--]=dd[i];
			fd(i,1,dds)dd[T2[fi[ee[i].x]>>8]--]=ee[i];
		}
		int zs=0;
		fo(i,1,dds){
			if(dd[i].x!=ee[zs].x)ee[++zs]=(nod){dd[i].x,0,0};
			ee[zs].c+=dd[i].c;ee[zs].q+=dd[i].q;
		}
		fo(i,1,zs)dd[i]=ee[i];
		ss=0;sds=0;
		fo(i,1,zs){
			x=dd[i].x;
			cc[x]+=dd[i].c;
			qq[x]+=dd[i].q;
			if(!ss){
				s[++ss]=x;
				continue;
			}
			if(en[s[ss]]>fi[x]){
				s[++ss]=x;
				continue;
			}
			for(;ss&&en[s[ss]]<fi[x];)sfa[s[ss]]=s[ss-1],sd[++sds]=s[ss],--ss;
			int lc=lca(s[ss+1],x);
			sfa[s[ss+1]]=lc;
			if(lc!=s[ss])s[++ss]=lc;
			s[++ss]=x;
		}
		fd(i,1,ss)sfa[s[i]]=s[i-1],sd[++sds]=s[i];
		fo(i,1,sds){
			x=sd[i];
			ans+=(ll)(de[x]-de[sfa[x]])*cc[x]*qq[x];
			cc[sfa[x]]+=cc[x];
			qq[sfa[x]]+=qq[x];
		}
		fo(i,1,sds)cc[sd[i]]=qq[sd[i]]=0;
	}
}
struct tree{
	int fa[M],fh[M],eu[M],fi[M],es,ct[M];
	ll su[M];
	vector<int>e[M];
	void link(int x,int y){e[x].pb(y);fa[y]=x;}
	void dfs(int x){
		eu[++es]=x;fh[es]=1;fi[x]=es;
		fo(i,0,e[x].size()-1)dfs(e[x][i]);
		eu[++es]=x;fh[es]=-1;
	}
	void init(){
		dfs(nn+1);
		fo(i,1,es){
			ct[i]+=ct[i-1]+fh[i];
			su[i]+=su[i-1]+fh[i]*graph::de[eu[i]];
		}
	}
}T[2];
int Bk,be[M+5],sta[M/G+5],end[M/G+5],E,_n,cnt1,cnt2;
int v[2][M/G+5][2][M+5];
ll v2[2][M/G+5][2][M/G+5];
ll sum1,sum2;
void get_id(int &v,int l,int r){
	v=++ts;le[v]=l;ri[v]=r;
	if(!L[l])L[l]=v;
	if(!R[r])R[r]=v;
	if(l==r){
		dy[l]=v;
		return;
	}
	int m;scanf("%d",&m);
	get_id(s[v][0],l,m);
	get_id(s[v][1],m+1,r);
}
void dfs(int x){
	de[x]=de[fa[x]]+1;ld[x]=1;
	fo(o,0,1)if(s[x][o]){
		int y=s[x][o];
		fa[y]=x;
		dfs(y);
		ld[x]+=ld[y];
		if(ld[y]>ld[hv[x]])hv[x]=y;
	}
}
void dfs2(int x,int tp){
	ld[x]=tp;
	if(!hv[x])return;
	dfs2(hv[x],tp);
	fo(o,0,1)if(s[x][o]&&s[x][o]!=hv[x])dfs2(s[x][o],s[x][o]);
}
int get_lca(int x,int y){
	for(;ld[x]!=ld[y];x=fa[ld[x]])if(de[ld[x]]<de[ld[y]])swap(x,y);
	return de[x]<de[y]?x:y;
}
void work(int &a,int &b,int &A,int &B,int &ct,ll &su){
	a=dy[a-1];b=dy[b+1];
	A=get_lca(a,b);
	B=s[A][0];
	A=s[A][1];
	A=T[0].fi[A]+1;a=T[0].fi[a]-1;
	B=T[1].fi[B]+1;b=T[1].fi[b]-1;
	ct=(A<=a?T[0].ct[a]-T[0].ct[A-1]:0)+(B<=b?T[1].ct[b]-T[1].ct[B-1]:0);
	su=(A<=a?T[0].su[a]-T[0].su[A-1]:0)+(B<=b?T[1].su[b]-T[1].su[B-1]:0);
}
void get_ans1(int o,int u,int a,int A,int c,int C,int a0,int a1,int c0,int c1){
	a=be[a]-1+a1;A=be[A]-a0;c=be[c]-1+c1;C=be[C]-c0;
	if(A<a&&C<c)
		ans+=v2[o][a][u][c]-v2[o][a][u][C]-v2[o][A][u][c]+v2[o][A][u][C];
}
void qu(int o,int u,int a,int A,int c,int C,int a0,int a1,int c0,int c1){
	int be0=be[C]-c0,be1=be[c]-1+c1;
	if(be0<be1){
		if(be[a]==be[A])
			if(a0){
				fo(i,sta[be[A]],A-1)ans-=v[u][be1][o][i]-v[u][be0][o][i];
				fo(i,a+1,end[be[a]])ans-=v[u][be1][o][i]-v[u][be0][o][i];
			}else 
				fo(i,A,a)ans+=v[u][be1][o][i]-v[u][be0][o][i];
		else{
			if(a0)
				fo(i,sta[be[A]],A-1)ans-=v[u][be1][o][i]-v[u][be0][o][i];
			else
				fo(i,A,end[be[A]])ans+=v[u][be1][o][i]-v[u][be0][o][i];
			if(a1)
				fo(i,a+1,end[be[a]])ans-=v[u][be1][o][i]-v[u][be0][o][i];
			else 
				fo(i,sta[be[a]],a)ans+=v[u][be1][o][i]-v[u][be0][o][i];
		}
	}
}
void get_ans2(int o,int u,int a,int A,int c,int C,int a0,int a1,int c0,int c1){
	if(a<A||c<C)return;
	qu(o,u,a,A,c,C,a0,a1,c0,c1);
	qu(u,o,c,C,a,A,c0,c1,a0,a1);
}
void get_dl(int a,int A,int o,int u,int a0,int a1){
	if(a<A)return;
	if(be[a]==be[A]){
		if(a0){
			fo(i,sta[be[A]],A-1)if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,-T[o].fh[i]}:(nod){T[o].eu[i],-T[o].fh[i],0};
			fo(i,a+1,end[be[a]])if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,-T[o].fh[i]}:(nod){T[o].eu[i],-T[o].fh[i],0};
		}else
			fo(i,A,a)if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,T[o].fh[i]}:(nod){T[o].eu[i],T[o].fh[i],0};
	}else{
		if(a0){
			fo(i,sta[be[A]],A-1)if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,-T[o].fh[i]}:(nod){T[o].eu[i],-T[o].fh[i],0};
		}else
			fo(i,A,end[be[A]])if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,T[o].fh[i]}:(nod){T[o].eu[i],T[o].fh[i],0};
		if(a1){
			fo(i,a+1,end[be[a]])if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,-T[o].fh[i]}:(nod){T[o].eu[i],-T[o].fh[i],0};
		}else 
			fo(i,sta[be[a]],a)if(T[o].eu[i]<=nn)dd[++dds]=u?(nod){T[o].eu[i],0,T[o].fh[i]}:(nod){T[o].eu[i],T[o].fh[i],0};
	}
}
void init2(){
	s[nn+1][0]=nn+2;s[nn+1][1]=nn+4;
	s[nn+2][0]=nn+3;s[nn+2][1]=rt;
	R[0]=nn+3;R[n]=nn+2;L[n+1]=nn+4;
	dy[0]=nn+3;dy[n+1]=nn+4;
	fo(i,1,nn){
		T[0].link(L[ri[i]+1],i);
		T[1].link(R[le[i]-1],i);
	}
	T[0].link(L[1],nn+3);
	T[0].link(nn+4,nn+2);
	T[0].link(nn+1,nn+4);
	
	T[1].link(nn+2,nn+4);
	T[1].link(nn+1,nn+2);
	T[1].link(nn+1,nn+3);
	T[0].init();T[1].init();
	dfs(nn+1);dfs2(nn+1,nn+1);
}
void get_op(int &a0,int &a1,int A,int a){
	a0=a1=0;
	if(a<A)return;
	if(be[a]==be[A]){
		if((end[be[a]]-sta[be[a]])-(a-A)<a-A)a0=1,a1=1;
	}else{
		if(A-sta[be[A]]<end[be[A]]-A)a0=1;
		if(end[be[a]]-a<a-sta[be[a]])a1=1;
	}
}
int main(){
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	cin>>n>>q;nn=n*2-1;
	get_id(rt,1,n);
	
	fo(i,2,nn)scanf("%d%d",&a,&b),graph::link(a,b);
	graph::init();
	init2();
	E=T[0].es;Bk=100;
	fo(i,1,E)be[i]=i/Bk+1,(!sta[be[i]]?sta[be[i]]=i:0),end[be[i]]=i;
	_n=be[E];
	fo(o,0,1)
		fo(i,1,_n){
			memset(su,0,(E+1)*4);
			fo(j,sta[i],end[i])
				su[T[o].eu[j]]+=T[o].fh[j];
			graph::pre();
			fo(u,0,1)
				fo(j,1,E){
					v[o][i][u][j]=v[o][i-1][u][j]+su[T[u].eu[j]]*T[u].fh[j];
					v2[o][i][u][be[j]]+=v[o][i][u][j];
				}
		}
	fo(o,0,1)fo(i,1,_n)
		fo(u,0,1)fo(j,1,_n)
			v2[o][i][u][j]+=v2[o][i][u][j-1];
	for(;q--;){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		ans=0;
		work(a,b,A,B,cnt1,sum1);
		work(c,d,C,D,cnt2,sum2);
		
		get_op(a0,a1,A,a);
		get_op(b0,b1,B,b);
		get_op(c0,c1,C,c);
		get_op(d0,d1,D,d);
		
		get_ans1(0,0,a,A,c,C,a0,a1,c0,c1);
		get_ans1(0,1,a,A,d,D,a0,a1,d0,d1);
		get_ans1(1,0,b,B,c,C,b0,b1,c0,c1);
		get_ans1(1,1,b,B,d,D,b0,b1,d0,d1);
		
		get_ans2(0,0,a,A,c,C,a0,a1,c0,c1);
		get_ans2(0,1,a,A,d,D,a0,a1,d0,d1);
		get_ans2(1,0,b,B,c,C,b0,b1,c0,c1);
		get_ans2(1,1,b,B,d,D,b0,b1,d0,d1);
		
		dds=0;
		get_dl(a,A,0,0,a0,a1);
		get_dl(b,B,1,0,b0,b1);
		get_dl(c,C,0,1,c0,c1);
		get_dl(d,D,1,1,d0,d1);
		
		graph::get_tree();
		printf("%lld\n",sum1*cnt2+sum2*cnt1-ans*2);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值