BZOJ 4012: [HNOI2015]开店

这叫啥?可持久化树链剖分?

首先忽略掉那个年龄的限制

那就是求所有点到某点的路径之和

这个画个图会发现是 Σdepth[i]+n*depth[u]-2*Σdepth(lca(u,i))

前两个可以O(1)求,最后那个可以用树剖求。

方法就是每个点到根的路径测度+1,这个可以用线段树更新,然后u到根的路径上的测度与长度之积的和就是答案了。

但是出现了那个可恶的限制

不过嘛,我们有传说中的chairman algorithm

于是我们把它变成两个前缀相减,再用可持久化线段树求前缀和

空间O(n(logn)^2),时间O(n(logn)^2)

(由于这题空间不是很好说,于是我把pushdown操作干掉了,每次往下找的时候把标记加起来,然后就上首页了,感觉跑得飞快啊)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=13000000;
const int N=150000+5;
typedef long long ll;
struct Node{
	int lc,rc,cov,ti;
	ll sum;
}tr[M];
int tot;
int dep[N],top[N],fa[N],siz[N],son[N],pos[N],rank[N],cost[N],depth[N],sz;
struct Edge{int to,next,v;}e[N<<1];
int head[N],cnt;
ll len(int l,int r){
	return depth[rank[r]]-depth[fa[rank[l]]];
}
void ins(int u,int v,int w){
	e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;
}
void dfs(int u){
	siz[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;if(v==fa[u])continue;
		fa[v]=u;dep[v]=dep[u]+1;cost[v]=e[i].v;depth[v]=depth[u]+cost[v];
		dfs(v);
		siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs(int u,int tp){
	top[u]=tp;pos[u]=++sz;rank[sz]=u;
	if(son[u])dfs(son[u],tp);
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v!=son[u]&&v!=fa[u])dfs(v,v);
	}
}
ll update(int &o,int l,int r,int a,int b,int ti){
	if(tr[o].ti!=ti)tr[++tot]=tr[o],o=tot,tr[tot].ti=ti;
	if(l==a&&r==b){tr[o].cov++;tr[o].sum+=len(l,r);return len(l,r);}
	else{
		int mid=l+r>>1;
		ll add=0;
		if(b<=mid)add+=update(tr[o].lc,l,mid,a,b,ti);
		else if(mid<a)add+=update(tr[o].rc,mid+1,r,a,b,ti);
		else add+=update(tr[o].lc,l,mid,a,mid,ti)+update(tr[o].rc,mid+1,r,mid+1,b,ti);
		tr[o].sum+=add;
		return add;
	}
}
ll query(int o,int l,int r,int a,int b,int cov){
	if(l==a&&b==r)return tr[o].sum+len(l,r)*cov;
	else{
		int mid=l+r>>1;
		cov+=tr[o].cov;
		if(b<=mid)return query(tr[o].lc,l,mid,a,b,cov);
		else if(mid<a)return query(tr[o].rc,mid+1,r,a,b,cov);
		else return	query(tr[o].lc,l,mid,a,mid,cov)+query(tr[o].rc,mid+1,r,mid+1,b,cov);
	}
}
int root[N];
int n;
void update(int u,int ti){
	while(u){
		update(root[ti],1,n,pos[top[u]],pos[u],ti);
		u=fa[top[u]];
	}
}
ll query(int u,int ti){
	ll ans=0;
	while(u){
		ans+=query(root[ti],1,n,pos[top[u]],pos[u],0);
		u=fa[top[u]];
	}
	return ans;
}
int hash[N],id[N],rk[N];
ll sum[N];
bool cmp(int i,int j){
	return hash[i]<hash[j];
}
int findl(int l,int r,int x){
	while(l<=r){
		int mid=l+r>>1;
		if(hash[id[mid]]<x)l=mid+1;
		else r=mid-1;
	}
	return l-1;
}
int findr(int l,int r,int x){
	while(l<=r){
		int mid=l+r>>1;
		if(hash[id[mid]]<=x)l=mid+1;
		else r=mid-1;
	}
	return l-1;
}
ll ask(int u,int ti){
	return sum[ti]+(ll)ti*depth[u]-2*query(u,ti);
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int q,A;scanf("%d%d%d",&n,&q,&A);
	for(int i=1;i<=n;i++){
		scanf("%d",&hash[i]);
		id[i]=i;
	}
	sort(id+1,id+1+n,cmp);
	for(int i=1;i<n;i++){
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		ins(u,v,w);ins(v,u,w);
	}
	dfs(1);dfs(1,1);
	for(int i=1;i<=n;i++){
		root[i]=root[i-1];
		update(id[i],i);
		rk[id[i]]=i;
		sum[i]=sum[i-1]+depth[id[i]];
	}
	ll ans=0;
	while(q--){
		int u;ll a,b;scanf("%d%lld%lld",&u,&a,&b);
		int l=(a+ans)%A,r=(b+ans)%A;
		if(l>r)swap(l,r);
		l=findl(1,n,l);r=findr(1,n,r);
		ans=ask(u,r)-ask(u,l);
		printf("%lld\n",ans);
	}
	return 0;
}
这题好像能用动态点分治做哎,不过我比较懒(明明动态点分治更好写好吗)

你可能感兴趣的:(BZOJ 4012: [HNOI2015]开店)