【动态点分治】BZOJ4012 [HNOI2015]开店

【题目】
BZOJ
给定一棵 n n n个点带边权的树,同时每个节点有一个权 x x x Q Q Q次询问点权在 [ l , r ] [l,r] [l,r]的所有点到 u u u的距离和。强制在线,每个节点度数不超过三。
n , Q ≤ 2 × 1 0 5 n,Q\leq 2\times 10^5 n,Q2×105

【解题思路】
考虑动态点分治,由于一个点的度数不超过三,那么点分树上每个点的儿子也不超过三。
我们将每个分治中心内存的信息按排列顺序,那么每次询问就是一个前缀的差分。一个十分简单的想法就是在 vector \text{vector} vector上二分。

具体来说,我们将所有点权排序后,维护一个前缀边权和。询问时在每一层我们可以算出经过该层重心到达查询点的距离和,那显然这样子会算多,因此我们计算时要减去当前重心的子树中到分治结构父亲的距离和。还需要加上分治结构中除了上次处理的那棵子树之外的子树节点个数*上次重心到这次重心的距离(原树中两点距离)。

这样复杂度是 O ( n log ⁡ 2 n ) O(n\log ^2n) O(nlog2n)的。

如果不嫌麻烦就可持久化点分树,以点权顺序可持久化,这样每次查询是查询深度和之类的东西。这样做可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn),可以参考 CF757G \text{CF757G} CF757G

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

typedef long long ll;
const int N=2e5+10,INF=0x3f3f3f3f;
int fc[20],Log[N<<1];

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Tree
{
	int tot,ind;
	int head[N],pos[N];
	ll dis[N],md[20][N<<1];
	struct Tway{int v,w,nex;}e[N<<1];
	void add(int u,int v,int w)
	{
		e[++tot]=(Tway){v,w,head[u]};head[u]=tot;
		e[++tot]=(Tway){u,w,head[v]};head[v]=tot;
	}
	void dfs(int x,int f)
	{	
		pos[x]=++ind;md[0][ind]=dis[x];
		for(int i=head[x];i;i=e[i].nex) 
		{
			int v=e[i].v;
			if(v==f) continue;
			dis[v]=dis[x]+e[i].w;dfs(v,x);md[0][++ind]=dis[x];
		}
	}
	ll calc(int x,int y)
	{
		ll res=dis[x]+dis[y];
		x=pos[x];y=pos[y];
		if(x>y) swap(x,y);
		int t=Log[y-x+1];
		return res-min(md[t][x],md[t][y-fc[t]+1])*2;
	} 
}
using namespace Tree;

namespace DreamLolita
{
	int sum,rt,n,m,K;
	int siz[N],mx[N],vis[N],tf[N],a[N];
	ll ans;
	struct data
	{
		ll dis,sum;int val;
		data(int a=0,ll b=0,ll c=0):val(a),dis(b),sum(c){}
		bool operator <(const data&rhs)const{return val==rhs.val?dis==rhs.dis?sum<rhs.sum:dis<rhs.dis:val<rhs.val;}
	};
	vector<data>va[N],vb[N];
	void initst()
	{
		fc[0]=1;for(int i=1;i<19;++i) fc[i]=fc[i-1]<<1;
		for(int i=2;i<N<<1;++i) Log[i]=Log[i>>1]+1;
		for(int j=1;j<19;++j) for(int i=1;i+fc[j]-1<=ind;++i)
			md[j][i]=min(md[j-1][i],md[j-1][i+fc[j-1]]);
	}
	void getroot(int x,int f)
	{
		siz[x]=1;mx[x]=0;
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(vis[v] || v==f) continue;
			getroot(v,x);siz[x]+=siz[v];mx[x]=max(mx[x],siz[v]);
		}
		mx[x]=max(mx[x],sum-siz[x]);
		if(mx[x]<mx[rt]) rt=x;
	}
	void solve(int x)
	{
		vis[x]=1;
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(vis[v]) continue;
			sum=siz[v];rt=0;getroot(v,x);tf[rt]=x;solve(rt);
		}
	}
	ll query(int x,int d)
	{
		//printf("now:%d %d\n",x,d);
		ll res=0;
		for(int i=x;i;i=tf[i])
		{
			//printf("now:%d\n",i);
			int t=upper_bound(va[i].begin(),va[i].end(),data(d,INF,INF))-va[i].begin()-1;
			//printf("get:%d\n",t);
			res+=va[i][t].sum+calc(i,x)*t;
		}
		for(int i=x;tf[i];i=tf[i])
		{
			//printf("now:%d\n",i);
			int t=upper_bound(vb[i].begin(),vb[i].end(),data(d,INF,INF))-vb[i].begin()-1;
			//printf("get:%d\n",t);
			res-=vb[i][t].sum+calc(tf[i],x)*t;
		}
		return res;
	}
	void solution()
	{
		n=read();m=read();K=read();
		for(int i=1;i<=n;++i) a[i]=read();
		for(int i=1,x,y,z;i<n;++i) x=read(),y=read(),z=read(),add(x,y,z);
		dfs(1,0);initst();
		mx[0]=sum=n;getroot(1,0);solve(rt);
		for(int i=1;i<=n;++i) for(int j=i;j;j=tf[j])
			va[j].pb(data(a[i],calc(i,j),0)),vb[j].pb(data(a[i],calc(i,tf[j]),0));
		for(int i=1;i<=n;++i)
		{
			va[i].pb(data(-1,0,0));va[i].pb(data(INF,0,0));
			vb[i].pb(data(-1,0,0));vb[i].pb(data(INF,0,0));
			sort(va[i].begin(),va[i].end());sort(vb[i].begin(),vb[i].end());
			//printf("i:%d\n",i);
			for(int j=1;j<(int)va[i].size();++j) va[i][j].sum=va[i][j-1].sum+va[i][j].dis;
			for(int j=1;j<(int)vb[i].size();++j) vb[i][j].sum=vb[i][j-1].sum+vb[i][j].dis;
		}
		while(m--)
		{
			int x=read(),a=(read()+ans)%K,b=(read()+ans)%K;if(a>b) swap(a,b);
			writeln(ans=query(x,b)-query(x,a-1));
		}
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ4012.in","r",stdin);
	freopen("BZOJ4012.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值