P13567 折跃点

Luogu 链接

题意

有一棵包含 \(n\) 个节点的以 \(1\) 为根的有根树,点有初始点权 \(w_i\)

我们称点 \(u\) 能经过 \(x\) 次连续折跃能到达点 \(v\),当且仅当从点 \(u\) 出发,经历过 \(x\) 条边能到达点 \(v\),并且过程中与点 \(1\) 的距离单调递增或单调递减。

现在有 \(q\) 次操作,操作有两种类型:

  • 操作 \(1\):对于所有能从点 \(u\) 通过 \(x\) 次连续折跃到达的点,将其点权加 \(y\)
  • 操作 \(2\):求所有能从点 \(u\) 通过 \(x\) 次连续折跃到达的点的点权之和。

数据范围:\(1\le u\le n\le3\times10^5\)\(1\le q\le3\times10^5\)\(1\le w_i,y\le10^9\)\(0\le x\le n\)

思路

注意到若点 \(u\) 能经过 \(x\) 次连续折跃能到达点 \(v\),则点 \(v\) 要么是点 \(u\)\(x\) 级祖先;要么是在点 \(u\) 的子树内,且深度恰比点 \(u\) 的深度大 \(x\)

对于修改、查询 \(x\) 级祖先是简单的,只需用倍增预处理,然后跳 \(x\) 级祖先维护即可。
关键是如何维护在点 \(u\) 子树内的点。

我们考虑进行一遍 DFS,并将一个点的 DFS 序插入到对应深度标号的平衡树里面。
由于一个子树内的 DFS 序是一段区间,我们可以在平衡树里直接 split 出来,所以原问题就变成了区间加区间求和。

祖先的修改、查询与子树内修改同理(变成了单点修改)。

注意当 \(x=0\) 时不要重复修改和查询。

可以做到 \(O(n)\) 预处理,\(O(q\log n)\) 处理询问,空间复杂度为 \(O(n)\)

程序

AC 记录

#include<bits/stdc++.h>
#define forUp(i,a,b) for(int i=(a);i<=(b);++i)
#define forUP(i,a,b) for(int i=(a);i<(b);++i)
#define forDown(i,a,b) for(int i=(a);i>=(b);--i)
#define forG(i,u,v) for(int i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define pb emplace_back
using ll=long long;using ull=unsigned long long;using uint=unsigned int;using db=double;using ld=long db;using pii=std::pair<int,int>;using pdi=std::pair<db,int>;using vl=__int128;using uvl=unsigned __int128;
constexpr int INF=0x3f3f3f3f,MINF=0xcfcfcfcf;constexpr long long INFLL=0x3f3f3f3f3f3f3f3f,MINFLL=0xcfcfcfcfcfcfcfcf;constexpr double INFDB=1e50,eps=1e-9;
template<class _Tp>void chkMax(_Tp &x,const _Tp &y){x<y?x=y:0;}template<class _Tp>void chkMin(_Tp &x,const _Tp &y){x>y?x=y:0;}
constexpr int N=3e5+10;int __test_num=1,__test_id;using namespace std;void __init();

int n,q,w[N],u,v,op,x,y;

int tot,root[N],lson[N],rson[N],sz[N],rnd[N],val[N];ll num[N],sum[N],add[N];
int newNode(int value,int weight){
	int node=++tot;
	lson[node]=rson[node]=0;
	sz[node]=1;
	rnd[node]=rand();
	val[node]=value;
	num[node]=sum[node]=weight;
	return node;
}
void pushup(int node){
	if(!node)return;
	sz[node]=sz[lson[node]]+sz[rson[node]]+1;
	sum[node]=sum[lson[node]]+sum[rson[node]]+num[node];
}
void modify(int node,ll tag){
	if(!node)return;
	add[node]+=tag;
	num[node]+=tag;
	sum[node]+=tag*sz[node];
}
void pushdown(int node){
	if(!node)return;
	if(add[node]){
		if(lson[node])modify(lson[node],add[node]);
		if(rson[node])modify(rson[node],add[node]);
		add[node]=0;
	}
}
void split(int node,int value,int &treap1,int &treap2){
	if(!node){treap1=treap2=0;return;}
	pushdown(node);
	if(val[node]<=value)treap1=node,split(rson[node],value,rson[node],treap2);
	else treap2=node,split(lson[node],value,treap1,lson[node]);
	pushup(node);
}
int merge(int treap1,int treap2){
	if(!treap1||!treap2)return treap1|treap2;
	if(rnd[treap1]<rnd[treap2]){
		pushdown(treap1);
		rson[treap1]=merge(rson[treap1],treap2);
		pushup(treap1);
		return treap1;
	}
	else{
		pushdown(treap2);
		lson[treap2]=merge(treap1,lson[treap2]);
		pushup(treap2);
		return treap2;
	}
}
void insert(int &rt,int value,int weight){
	int treap1=0,treap2=0;
	split(rt,value,treap1,treap2);
	rt=merge(merge(treap1,newNode(value,weight)),treap2);
}
void update(int &rt,int l,int r,int x){
	int treap1=0,treap2=0,treap3=0;
	split(rt,r,treap2,treap3);
	split(treap2,l-1,treap1,treap2);
	modify(treap2,x);
	rt=merge(merge(treap1,treap2),treap3);
}
ll query(int &rt,int l,int r){
	int treap1=0,treap2=0,treap3=0;
	split(rt,r,treap2,treap3);
	split(treap2,l-1,treap1,treap2);
	ll ans=sum[treap2];
	rt=merge(merge(treap1,treap2),treap3);
	return ans;
}

int cnt,head[N],to[N<<1],nxt[N<<1];
void addEdge(int u,int v){
	++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
	++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}

int dep[N],DFN,bg[N],ed[N],fa[N][20];
void dfs(int rt){
	bg[rt]=++DFN,dep[rt]=dep[fa[rt][0]]+1;
	insert(root[dep[rt]],bg[rt],w[rt]);
	for(int i=1;fa[rt][i-1];++i)fa[rt][i]=fa[fa[rt][i-1]][i-1];
	forG(i,rt,son){
		if(dep[son])continue;
		fa[son][0]=rt;
		dfs(son);
	}
	ed[rt]=DFN;
}
int kthfa(int u,int k){
	if(dep[u]-k<=0)return 0;
	int v=u;
	forUP(i,0,20)if(1<<i&k)v=fa[v][i];
	return v;
}

void __solve(int __test_id){
	scanf("%d%d",&n,&q);
	forUp(i,1,n)scanf("%d",&w[i]);
	forUP(i,1,n)scanf("%d%d",&u,&v),addEdge(u,v);
	dfs(1);
	while(q--){
		scanf("%d%d%d",&op,&u,&x);
		if(op==1){
			scanf("%d",&y);
			int kthpa=kthfa(u,x);
			if(kthpa)update(root[dep[kthpa]],bg[kthpa],ed[kthpa],y);
			if(x!=0&&dep[u]+x<=n&&root[dep[u]+x])update(root[dep[u]+x],bg[u],ed[u],y);
		}
		else{
			int kthpa=kthfa(u,x);ll ans=0;
			if(kthpa)ans+=query(root[dep[kthpa]],bg[kthpa],ed[kthpa]);
			if(x!=0&&dep[u]+x<=n&&root[dep[u]+x])ans+=query(root[dep[u]+x],bg[u],ed[u]);
			printf("%lld\n",ans);
		}
	}
}
signed main(){
	__init();
	forUp(i,1,__test_num)__solve(i);
	return 0;
}
void __init(){
	//const string __file_name="test";freopen((__file_name+".in").c_str(),"r",stdin);freopen((__file_name+".out").c_str(),"w",stdout);
	//scanf("%d",&__test_num);
}
posted @ 2025-08-05 10:41  LXcjh4998  阅读(16)  评论(0)    收藏  举报