CF1076E Solution

题目链接

题解

\(n,m\le 3\cdot 10^5\)可看出,此题需在\(logn\)的时间内进行区间修改与单点查询。而描述操作位置的只有距离(子树深度)一个变量,可以想到以深度为关键字建立线段树或树状数组。

设点\(i\)深度为\(pos_i\),对于每个操作将深度区间\([pos_{v_i},pos_{v_i}+d_i]+x_i\)即可。可以发现,若每个节点在将相关操作区间修改完毕后立刻计算答案,便可保证节点的修改不会影响到其祖先。因此区间修改时可直接将\([1,pos_{v_i}+d_i]+x_i\),这样用树状数组维护更加便捷。立刻计算答案还有另一个原因,为使修改不会影响到同级节点及其子节点,在回溯时需取消每个节点的修改。

AC代码

#include<bits/stdc++.h>
#define lowb(x) x&(-x)
#define int long long
using namespace std;
const int N=3e5+10;
struct node {int v,d,x;} q[N];//操作
int fst[N],nxt[2*N],v[2*N],cnt;
int t[N],ans[N],n;//t:树状数组
vector<int> qwq[N];//qwq[i]:d=i的操作下标
void add(int x,int y)
{
	v[++cnt]=y;
	nxt[cnt]=fst[x]; fst[x]=cnt;
}
void modif(int x,int y)
{
	while(x) {t[x]+=y; x-=lowb(x);}
}
int query(int x)
{
	int ans=0;
	while(x<=n) {ans+=t[x]; x+=lowb(x);}
	return ans;
}
void dfs(int x,int fa,int pos)
{
	int siz=qwq[x].size();
	for(int i=0;i<siz;i++) modif(min(pos+q[qwq[x][i]].d,n),q[qwq[x][i]].x);//修改操作
    //因为pos+d可能会大于n(最大深度),因此需要取min
	ans[x]=query(pos);
	for(int i=fst[x];i;i=nxt[i])
		if(v[i]!=fa) dfs(v[i],x,pos+1);
	for(int i=0;i<siz;i++) modif(min(pos+q[qwq[x][i]].d,n),-q[qwq[x][i]].x);//取消修改
}
signed main()
{
	int m,x,y;
	scanf("%lld",&n);
	for(int i=1;i<n;i++) 
	{
		scanf("%lld%lld",&x,&y);
		add(x,y); add(y,x);
	}
	scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&q[i].v,&q[i].d,&q[i].x);
		qwq[q[i].v].push_back(i); 
	}
	dfs(1,0,1);
	for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14369699.html