ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【洛谷P6329】【模板】点分树 | 震波

2020-12-28 20:35:37  阅读:219  来源: 互联网

标签:head 洛谷 int maxp tot 点分树 震波 size dis


题目

题目链接:https://www.luogu.com.cn/problem/P6329
在一片土地上有 \(n\) 个城市,通过 \(n-1\) 条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为 \(1\),其中第 \(i\) 个城市的价值为 \(value_i\)。

不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。

接下来你需要在线处理 \(m\) 次操作:

0 x k 表示发生了一次地震,震中城市为 \(x\),影响范围为 \(k\),所有与 \(x\) 距离不超过 \(k\) 的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。

1 x y 表示第 \(x\) 个城市的价值变成了 \(y\) 。

为了体现程序的在线性,操作中的 \(x\)、\(y\)、\(k\) 都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为 \(0\) 。

思路

点分树就是在点分治的基础上,将每次跳的重心与上一次跳的重心连边,构成一棵点分树。也就是一个点 \(x\) 的子节点是点分治时以 \(x\) 为重心的子树扔掉点 \(x\) 后,其余所有的树的重心。
由于点分治只会递归 \(\log n\) 层,所以点分树的深度也是 \(O(\log n)\) 的。
对于本题,构建出点分树,对于每一个点 \(x\),我们维护两棵动态开点线段树,第一棵的一个区间 \([l,r]\) 表示在点分树以 \(x\) 为根的子树中,原树上与 \(x\) 距离在 \([l,r]\) 的点的权值和;第二棵线段树区间 \([l,r]\) 表示在点分树以 \(x\) 为根的子树中,原树上与 \(x\) 在点分树上的父亲之间的距离在 \([l,r]\) 的点的权值和。
对于修改操作,我们从点 \(x\) 不断往点分树上父亲跳,然后维护两棵线段树的值即可。
对于询问操作,我们依然从点 \(x\) 开始网上跳,对于跳到的一个节点 \(a\),设上一个调到的节点 \(b\),那么 \(a\) 会造成的贡献为距离 \(a\) 不超过 \(k-dis_{a,x}\) 的点。但是在 \(b\) 中已经有一部分点背计算过了,这样就会导致重复计算,所以还要减去 \(b\) 的第二棵线段树中不超过 \(k-dis_{a,x}\) 的点。
这样就可以在 \(O(n\log^2n)\) 的复杂度内计算出答案了。
由于这种做法常数较大,我们可以用 ST 表预处理 LCA,每次询问可以 \(O(1)\) 查,并且动态开点线段树可以改为离散化后的树状数组。注意每一个树状数组的大小应当分别离散化,且离散化后大小应为其点分树内子树大小。这样空间复杂度是 \(O(n\log n)\) 的。

代码

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

const int N=200010,LG=18,Inf=1e9;
int head[N],size[N],dfn[N],maxp[N],fa[N],dep[N],val[N],lg[N],st[N][LG+1];
int n,m,tot,rt,last;
bool vis[N];
vector<int> dis[2][N];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void dfs1(int x,int f)
{
	st[++tot][0]=x; dfn[x]=tot; dep[x]=dep[f]+1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=f)
		{
			dfs1(v,x);
			st[++tot][0]=x;
		}
	}
}

void getst()
{
	for (int i=tot;i>=1;i--)
		for (int j=1;i+(1<<j)-1<=tot;j++)
			if (dep[st[i][j-1]]<dep[st[i+(1<<j-1)][j-1]])
				st[i][j]=st[i][j-1];
			else
				st[i][j]=st[i+(1<<j-1)][j-1];
}

int lca(int x,int y)
{
	if (dfn[x]>dfn[y]) swap(x,y);
	int k=lg[dfn[y]-dfn[x]+1];
	if (dep[st[dfn[x]][k]]<dep[st[dfn[y]-(1<<k)+1][k]])
		return st[dfn[x]][k];
	else
		return st[dfn[y]-(1<<k)+1][k];
}

void findrt(int x,int f,int sum)
{
	size[x]=1; maxp[x]=0;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!vis[v] && v!=f)
		{
			findrt(v,x,sum);
			size[x]+=size[v];
			if (size[v]>maxp[x]) maxp[x]=size[v];
		}
	}
	if (sum-size[x]>maxp[x]) maxp[x]=sum-size[x];
	if (maxp[x]<maxp[rt] || !rt) rt=x;
}

void dfs2(int x,int f,int sum)
{
	fa[x]=f; vis[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!vis[v])
		{
			rt=0;
			int s=(size[v]<size[x]) ? size[v] : sum-size[x];
			findrt(v,x,s);
			dfs2(rt,x,s);
		}
	}
}

int getdis(int x,int y)
{
	return dep[x]+dep[y]-dep[lca(x,y)]*2;
}

struct BIT
{
	vector<int> c;
	
	void add(int x,int v)
	{
		for (int i=x;i<c.size();i+=i&-i)
			c[i]+=v;
	}
	
	int query(int x)
	{
		int sum=0;
		for (int i=x;i;i-=i&-i)
			sum+=c[i];
		return sum;
	}
}bit[2][N];

void update(int x,int v)
{
	for (int i=x;i;i=fa[i])
	{
		int p1=upper_bound(dis[0][i].begin(),dis[0][i].end(),getdis(x,i))-dis[0][i].begin();
		bit[0][i].add(min(p1,(int)dis[0][i].size()),v-val[x]);
		if (fa[i])
		{
			int p2=upper_bound(dis[1][i].begin(),dis[1][i].end(),getdis(fa[i],x))-dis[1][i].begin();
			bit[1][i].add(min(p2,(int)dis[1][i].size()),v-val[x]);
		}
	}
	val[x]=v;
}

int query(int x,int k)
{
	int ans=0;
	for (int i=x,j=0;i;j=i,i=fa[i])
	{
		int d=getdis(x,i);
		if (d>k) continue;
		int p1=upper_bound(dis[0][i].begin(),dis[0][i].end(),k-d)-dis[0][i].begin();
		ans+=bit[0][i].query(min(p1,(int)dis[0][i].size()));
		if (j)
		{
			int p2=upper_bound(dis[1][j].begin(),dis[1][j].end(),k-d)-dis[1][j].begin();
			ans-=bit[1][j].query(min(p2,(int)dis[1][j].size()));
		}
	}
	return ans;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&val[i]);
	for (int i=1,x,y;i<n;i++) 
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	tot=0; dfs1(1,0);
	getst();
	for (int i=2;i<=tot;i++)
		lg[i]=lg[i>>1]+1;
	findrt(1,0,n);
	dfs2(rt,0,n);
	for (int i=1;i<=n;i++)
		for (int j=i;j;j=fa[j])
		{
			dis[0][j].push_back(getdis(i,j));
			if (fa[j]) dis[1][j].push_back(getdis(i,fa[j]));
		}
	for (int i=1;i<=n;i++)
	{
		sort(dis[0][i].begin(),dis[0][i].end());
		sort(dis[1][i].begin(),dis[1][i].end());
		unique(dis[0][i].begin(),dis[0][i].end());
		unique(dis[1][i].begin(),dis[1][i].end());
		for (int j=0;j<=dis[0][i].size()+1;j++)
			bit[0][i].c.push_back(0);
		for (int j=0;j<=dis[1][i].size()+1;j++)
			bit[1][i].c.push_back(0);
	}
	for (int i=1;i<=n;i++)
	{
		int temp=val[i]; val[i]=0;
		update(i,temp);
	}
	while (m--)
	{
		int opt,x,y;
		scanf("%d%d%d",&opt,&x,&y);
		x^=last; y^=last;
		if (!opt) printf("%d\n",last=query(x,y));
			else update(x,y);
	}
	return 0;
}

标签:head,洛谷,int,maxp,tot,点分树,震波,size,dis
来源: https://www.cnblogs.com/stoorz/p/14203444.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有