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


    题目

    题目链接: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(nlog^2n)) 的复杂度内计算出答案了。
    由于这种做法常数较大,我们可以用 ST 表预处理 LCA,每次询问可以 (O(1)) 查,并且动态开点线段树可以改为离散化后的树状数组。注意每一个树状数组的大小应当分别离散化,且离散化后大小应为其点分树内子树大小。这样空间复杂度是 (O(nlog 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
    ",last=query(x,y));
    			else update(x,y);
    	}
    	return 0;
    }
    
  • 相关阅读:
    elasticsearch query 和 filter 的区别
    java 模拟简单搜索
    filterBuilders 构建过滤器query
    elasticsearch java 索引操作
    lesson4:利用jmeter来压测数据库
    lesson3:使用java代码的方式对不能识别的协议进行压力测试
    lession2:使用HTTP Cookie 管理器来传递cookies值
    lesson1:压测普通网页
    php mysql find_in_set函数 检索单子段 逗号分隔序列
    写出一种排序算法(要写出代码),并说出优化它的方法。(新浪面试题)
  • 原文地址:https://www.cnblogs.com/stoorz/p/14203444.html
Copyright © 2020-2023  润新知