[BZOJ4012][主席树][树链剖分]HNOI2015:开店

BZOJ4012

可以点分树做,也可以树链剖分+主席树
询问 ∑ v ∈ [ l , r ] d i s ( u , v ) \sum_{v∈[l,r]}{dis(u,v)} v[l,r]dis(u,v)就是求 ( r − l + 1 ) ∗ d e p [ u ] + ∑ v ∈ [ l , r ] d e p [ v ] − 2 ∗ ∑ v ∈ [ l , r ] d e p [ l c a ( u , v ) ] (r-l+1)*dep[u]+\sum_{v∈[l,r]}dep[v]-2*\sum_{v∈[l,r]}{dep[lca(u,v)]} (rl+1)dep[u]+v[l,r]dep[v]2v[l,r]dep[lca(u,v)],前面两项 O ( 1 ) O(1) O(1),最后一项实际上是把每个点到根的路径上的所有点打上一个标记,然后询问u到根节点的路径上的标记数,如果没有年龄的限制,就直接线段树做了
有限制的话,按照年龄排序建主席树,然后区间查询就很简单了

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1.5e5+5;
ll presum[N],sum[N];
namespace President_tree{
	struct seg{int ls,rs,cnt;ll sum;}tr[N*150];int tot;
	#define lc(k) tr[k].ls
	#define rc(k) tr[k].rs
	void build(int &k,int l,int r){
		k=++tot;
		if(l==r) return;
		int mid=l+r>>1;
		build(lc(k),l,mid);build(rc(k),mid+1,r);
	}
	void ins(int &rt1,int l,int r,int ql,int qr){
		tr[++tot]=tr[rt1];
		if(l==ql && r==qr){++tr[rt1=tot].cnt;return;}
		tr[rt1=tot].sum+=sum[qr]-sum[ql-1];
		int mid=l+r>>1;
		if(qr<=mid) ins(lc(rt1),l,mid,ql,qr);
		else if(ql>mid) ins(rc(rt1),mid+1,r,ql,qr);
		else ins(lc(rt1),l,mid,ql,mid),ins(rc(rt1),mid+1,r,mid+1,qr);
	}
	ll query(int rt,int l,int r,int ql,int qr){
		ll res=1ll*(sum[qr]-sum[ql-1])*tr[rt].cnt;
		if(l==ql && r==qr) return res+tr[rt].sum;
		int mid=l+r>>1;
		if(qr<=mid) return res+query(lc(rt),l,mid,ql,qr);
		else if(ql>mid) return res+query(rc(rt),mid+1,r,ql,qr);
		else return res+query(lc(rt),l,mid,ql,mid)+query(rc(rt),mid+1,r,mid+1,qr);
	}
}
using namespace President_tree;
int n,rt[N];
namespace tree{
	int vis[N<<1],nxt[N<<1],head[N],c[N<<1],tot=0;
	inline void add(int x,int y,int z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
	int siz[N],fa[N],hson[N],dep[N],pt[N];
	void dfs1(int v){
		pt[v]=siz[v]=1;
		for(int i=head[v];i;i=nxt[i]){
			int y=vis[i];
			if(pt[y]) continue;
			fa[y]=v;dep[y]=dep[v]+c[i];
			dfs1(y);
			siz[v]+=siz[y];
			if(siz[y]>siz[hson[v]]) hson[v]=y;
		}
	}
	int dfn[N],id[N],sign=0,top[N];
	void dfs2(int v){
		dfn[v]=++sign,sum[sign]=dep[v]-dep[fa[v]];
		if(hson[v]) top[hson[v]]=top[v],dfs2(hson[v]);
		for(int i=head[v];i;i=nxt[i])
			if(!top[vis[i]]) top[vis[i]]=vis[i],dfs2(vis[i]);
	}
	inline ll ask(int rt1,int v){
		ll res=0;
		while(top[v]!=1) res+=query(rt[rt1],1,n,dfn[top[v]],dfn[v]),v=fa[top[v]];
		return res+query(rt[rt1],1,n,1,dfn[v]);
	}
}
using namespace tree;
struct point{
	int age,id;
	point(){}
	point(int _age,int _id):age(_age),id(_id){}
}p[N];
inline bool operator < (point a,point b){return a.age==b.age?a.id<b.id:a.age<b.age;}
ll ans=0;
int main(){
	n=read();int q=read(),mod=read();
	for(int x,i=1;i<=n;i++) x=read(),p[i]=point(x,i);
	sort(p+1,p+n+1);
	for(int x,y,z,i=1;i<n;i++){
		x=read();y=read();z=read();
		add(x,y,z);add(y,x,z);
	}
	dfs1(1);top[1]=1;dfs2(1);
	for(int i=1;i<=n;i++) sum[i]+=sum[i-1],presum[i]=presum[i-1]+dep[p[i].id];
	build(rt[0],1,n);
	for(int i=1;i<=n;i++){
		int v=p[i].id;rt[i]=rt[i-1];
		while(top[v]!=1) ins(rt[i],1,n,dfn[top[v]],dfn[v]),v=fa[top[v]];
		ins(rt[i],1,n,1,dfn[v]);
	}
	while(q--){
		int u=read(),l=read(),r=read();
		l=(1ll*l+ans)%mod,r=(1ll*r+ans)%mod;
		if(l>r) swap(l,r);
		l=lower_bound(p+1,p+n+1,point(l,0))-p;
		r=upper_bound(p+1,p+n+1,point(r,n))-p-1;
		ans=1ll*(r-l+1)*dep[u]+presum[r]-presum[l-1]-2*(ask(r,u)-ask(l-1,u));
		cout<<ans<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值