题面:#6073. 「2017 山东一轮集训 Day5」距离

 

题解

好题

又是原题重测的题

大概就是让我们求

感觉一点思路都没有啊。。。

 

那我们先来看一个超级弱化版的题目([LNOI2014]LCA)

它是让我们求

这个题目相比之下就简单了许多

关于计算dep[LCA(i,z)]

我们可以先把i到1的路径上的所有点权都加一,再来求z到1路径的点权和

由于这道题可以利用前缀和来求答案

\sum_{i=l}^rdep[LCA(i,z)]=\sum_{i=1}^rdep[LCA(i,z)]-\sum_{i=1}^{l-1}dep[LCA(i,z)]

所以我们只需要离线之后把一个询问拆成两个

依次把1~i加入到树中(加一个点x,就利用树链剖分+线段树把x到1的路径所有点+1)

然后查询z到1路径的点权和即可

 

再来考虑这道题

 

似乎还是有点难啊

再来一个3级弱化版的题目

给出l,r,x,求l到r路径上每个点到x的距离之和,所有边权为1,可以离线

这个问题其实有很简单的数学做法,但是由于与此题思路无关,在这里不展开

我们稍加思索,就可以发现

\sum_{i\in path(l,r)}dis(i,x)=\sum_{i\in path(1,l)}dis(i,x)+\sum_{i\in path(1,r)}dis(i,x)-\sum_{i\in path(1,lca))}dis(i,x)-\sum_{i\in path(1,fa[lca]))}dis(i,x)

dis(i,x)=dis(1,i)+dis(1,x)-2*dis(1,lca)

显然dis(1,i)和dis(1,x)是非常好做的,也支持点到根路径的快速求和(预处理即可)

而dis(1,lca)就可以利用与上一道题的类似的方法

如果可以离线的话,我们就dfs整棵树,每新遇到一个点就把它到根节点的路径的所有点+1(出栈的时候就-1)

每一个询问都可以拆分成4个来做

dfs到一个点,就先把当前点加入树链剖分的线段树里面O(log^2n),再对这个点上的询问进行求值,均摊O(log^2n)

然后就可以O(nlog^2n)过掉此题

 

再来一个二级弱化版的题目

给出l,r,x,求l到r路径上每个点到x的距离之和,所有边权为1,强制在线

我们发现,上一道题之所以可以到一个点求出其有关询问的值,是因为我们把这个状态下线段树维护好了

我们又发现,一个点父亲状态的线段树与儿子状态的线段树,只相差了一个链剖区间+1(即把logn个区间+1)的操作

所以我们想到了可持久化线段树,每次都以当前点的父亲线段树为前置版本来进行修改

这样就可以在O(nlog^2n)的空间复杂度里保存每个点对应的线段树

直接在线查询即可

注意可持久化线段树不能写懒标记下传(因为可能下传到过去版本的线段树里)(除非你下传的时候也新建节点)

只能标记永久化

 

再来一个一级弱化版的题目

给出l,r,x,求l到r路径上每个点到x的距离之和,边权任意,强制在线

我们这次只需要在线段树上动手脚了

由于每个点所增加的权值是固定不变的(这个权值就是它到它父亲的距离)

我们只需要预处理一个dfs序的前缀和,在插入区间的时候把线段树当前节点的值加上ql到qr的前缀和即可

 

最后我们回到原题

给出l,r,x,求l到r路径上每个点的映射点到x的距离之和,边权任意,强制在线

其实我们最后发现这个映射根本不是难点

只需要在建可持久化线段树的时候把点x代为p[x],照样利用链剖来爬链修改线段树就可以了

 

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
#define LL long long
int n,Q;
int fir[N],to[2*N],nxt[2*N],cd[2*N],cnt;
void adde(int a,int b,int c)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cd[cnt]=c;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cd[cnt]=c;
}
int fa[N],son[N],dep[N],siz[N],top[N];
int val[N];LL sum[N],dis[N],dsum[N];
void dfs1(int u)
{
	dep[u]=dep[fa[u]]+1;
	siz[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]){
			fa[v]=u;
			val[v]=cd[p];
			dis[v]=dis[u]+1ll*cd[p];
			dfs1(v);
			siz[u]+=siz[v];
			if(siz[son[u]]<siz[v])
				son[u]=v;
		}
	}
}
int dfn[N],dc;
void dfs2(int u)
{
	dfn[u]=++dc;
	if(son[u])top[son[u]]=top[u],dfs2(son[u]);
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=fa[u]&&v!=son[u])
			top[v]=v,dfs2(v);
	}
}
int LCA(int x,int y)
{
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
struct node{
	int l,r,la;
	LL x;
}a[N<<7];
int T[N],P[N],tot;
void insert(int &i,int l,int r,int ql,int qr)
{
	a[++tot]=a[i];i=tot;
	if(ql==l&&qr==r){a[i].la++;return;}
	a[i].x+=sum[qr]-sum[ql-1];
	int mid=(l+r)>>1;
	if(qr<=mid)insert(a[i].l,l,mid,ql,qr);
	else if(ql>mid)insert(a[i].r,mid+1,r,ql,qr);
	else insert(a[i].l,l,mid,ql,mid),insert(a[i].r,mid+1,r,mid+1,qr);
}
LL query(int i,int l,int r,int ql,int qr)
{
	LL ret=(sum[qr]-sum[ql-1])*a[i].la;
	if(ql==l&&qr==r)return ret+a[i].x;
	int mid=(l+r)>>1;
	if(qr<=mid)return ret+query(a[i].l,l,mid,ql,qr);
	else if(ql>mid)return ret+query(a[i].r,mid+1,r,ql,qr);
	else return ret+query(a[i].l,l,mid,ql,mid)+query(a[i].r,mid+1,r,mid+1,qr);
}
void dfs(int u)
{
	T[u]=T[fa[u]];int x=P[u];
	dsum[u]=dsum[fa[u]]+dis[x];
	while(top[x]){
		insert(T[u],1,n,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	for(int v,p=fir[u];p;p=nxt[p])
		if((v=to[p])!=fa[u]) dfs(v);
}
LL QP(int x,int k)
{
	LL ret=dis[k]*dep[x]+dsum[x];
	while(top[k]){
		ret-=2ll*query(T[x],1,n,dfn[top[k]],dfn[k]);
		k=fa[top[k]];
	}
	return ret;
}
int main()
{
	int tp,i;LL u,v,k,ans=0;
	scanf("%d%d%d",&tp,&n,&Q);
	for(i=1;i<n;i++){
		scanf("%lld%lld%lld",&u,&v,&k);
		adde(u,v,k);
	}
	for(i=1;i<=n;i++)scanf("%d",&P[i]);
	dfs1(1);top[1]=1;dfs2(1);
	for(i=1;i<=n;i++)sum[dfn[i]]=val[i];
	for(i=1;i<=n;i++)sum[i]+=sum[i-1];
	dfs(1);
	while(Q--){
		scanf("%lld%lld%lld",&u,&v,&k);
		if(tp)u^=ans,v^=ans,k^=ans;
		int lca=LCA(u,v);
		ans=QP(u,k)+QP(v,k)-QP(lca,k)-QP(fa[lca],k);
		printf("%lld\n",ans);
	}
}