【HNOI 2015】开店 / 题解

【HNOI 2015】开店 / 题解

Description

风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。

很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n 个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,其中第 i 个地方的妖怪年龄是 $x_i$​。妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u(u 为编号),然后在 u 开一家面向年龄在 L 到 R 之间(即年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u 这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个称为这个开店方案的方便值。

幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

Simplified Description

给你一棵 $n$ 个点树,每个点都有一个权值。 有 $m$ 次询问,每次询问给你一个点,问这个点到所有权值在 $L$ 到 $R$ 之间 的点的距离和为多少。强制在线。

Idea

先将答案差分,考虑权值在 $[1,r]$ 内点到给定点的距离之和,可以用如下式子表示

$$\sum_{i=1}^r (dep_u+dep_i-2\times dep_{lca})=\sum_{i=1}^r dep_i+dep_u\times r+\sum dep_{lca}$$

发现除了 $\sum dep_{lca}$ 都挺好求的。

这时我们可以将点按照权值排序,然后依次插入。插入后它会给每个节点造成它自己深度的贡献,用树链剖分套线段树的区间操作即可。

因为我们需要询问在一段区间内的答案,因此需要将线段树可持久化。此时我们换用带修主席树维护。

时间复杂度 $\Theta(n\log^2n)$。

Code

#include <bits/stdc++.h>
#define N 200010
#define inf INT_MAX
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
int son[N],fa[N],siz[N],top[N],fv[N],num[N];
int cnt,head[N],id[N],rt[N],n,q,A;
ll sdep[N],dep[N],lastans;
ll read(){
	ll x=0,flag=1;char ch=getchar();
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if (ch=='-') flag=-1,ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch-'0'),ch=getchar();
	return x*flag;
}
struct Mon{
	int age,id;
	Mon(int a=0,int b=0){
		age=a,id=b;
	}
	bool operator < (const Mon &x) const{
		return age<x.age;
	}
}mon[N];
struct Node{
	int ls,rs,lazy;
	ll sum,esum;
}t[N<<7];
struct Edge{
	int to,next,val;
	Edge(int a=0,int b=0,int c=0){
		to=a,next=b,val=c;
	}
}l[N<<1];
void Add(int a,int b,int c){
	l[++cnt]=Edge(b,head[a],c);head[a]=cnt;
	l[++cnt]=Edge(a,head[b],c);head[b]=cnt;
}
void Pre_Work(int u,int f){
	fa[u]=f,siz[u]=1;
	int mx=-1;
	for (int i=head[u];i;i=l[i].next){
		int v=l[i].to;if (v==f) continue;
		dep[v]=dep[u]+l[i].val;fv[v]=l[i].val;
		Pre_Work(v,u);siz[u]+=siz[v];
		if (siz[v]>mx) mx=siz[v],son[u]=v;
	}
}
void Re_Build(int u,int topf){
	top[u]=topf,id[u]=++cnt;num[cnt]=fv[u];
	if (son[u]) Re_Build(son[u],topf);
	for (int i=head[u];i;i=l[i].next){
		int v=l[i].to;
		if (v!=son[u]&&v!=fa[u])
			Re_Build(v,v);
	}
}
void Build(int &p,int l,int r){
	int pre=p;p=++cnt;t[p]=t[pre];
	if (l==r) {t[p].esum=num[l];return;}
	Build(t[p].ls,l,mid);
	Build(t[p].rs,mid+1,r);
	t[p].esum=t[t[p].ls].esum+t[t[p].rs].esum;
}
void Update(int &p,int l,int r,int L,int R){
	int pre=p;p=++cnt;t[p]=t[pre];
	if (L<=l&&r<=R){
		++t[p].lazy;
		t[p].sum+=t[p].esum;
		return;
	}
	if (mid>=L) Update(t[p].ls,l,mid,L,R);
	if (mid<R) Update(t[p].rs,mid+1,r,L,R);
	t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum+t[p].esum*t[p].lazy;
}
ll Query(int p,int l,int r,int L,int R,int add){
	if (L<=l&&r<=R) return t[p].sum+t[p].esum*add;
	add+=t[p].lazy;
	if (mid>=R) return Query(t[p].ls,l,mid,L,R,add);
	if (mid<L) return Query(t[p].rs,mid+1,r,L,R,add);
	return Query(t[p].ls,l,mid,L,R,add)+Query(t[p].rs,mid+1,r,L,R,add);
}
ll Path_Query(int u,int ver){
	ll res=0;
	while(top[u]!=1){
		res+=Query(rt[ver],1,n,id[top[u]],id[u],0);
		u=fa[top[u]];
	}
	res+=Query(rt[ver],1,n,1,id[u],0);
	return res;
}
void Path_Update(int u,int ver){
	while(top[u]!=1){
		Update(rt[ver],1,n,id[top[u]],id[u]);
		u=fa[top[u]];
	}
	Update(rt[ver],1,n,1,id[u]);
}
ll Solve(int u,int ver){
	return sdep[ver]+dep[u]*ver-2*Path_Query(u,ver);
}
int main(){
	n=read(),q=read(),A=read();
	for (int i=1;i<=n;++i)
	mon[i].age=read(),mon[i].id=i;
	for (int i=2,a,b,c;i<=n;++i)
		a=read(),b=read(),c=read(),
		Add(a,b,c);cnt=0;
	Pre_Work(1,0);Re_Build(1,1);cnt=0;
	sort(mon+1,mon+1+n);
	Build(rt[0],1,n);
	for (int i=1;i<=n;++i)
		sdep[i]=sdep[i-1]+dep[mon[i].id],
		rt[i]=rt[i-1],Path_Update(mon[i].id,i);
	for (int i=1;i<=q;++i){
		int u;
		ll l,r;
		u=read(),l=read(),r=read();
		(l+=lastans)%=A,(r+=lastans)%=A;
		int L=min(l,r),R=max(l,r);
		l=lower_bound(mon+1,mon+1+n,Mon(L,0))-mon,
		r=upper_bound(mon+1,mon+1+n,Mon(R,inf))-mon-1;
		printf("%lld\n",lastans=Solve(u,r)-Solve(u,l-1));
	}
}