P13567 折跃点
题意
有一棵包含 \(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)\)。
程序
#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);
}

浙公网安备 33010602011771号