【题解】[CJOI2019] 树上查询(树状数组)
题目描述
班?小 A 需要管理信息组的日常纪律。所有人都在树形机房学习,树形机房的根节点为 1 。信息组的同学很多,但树
形机房的每个节点上有且仅有一个同学。小 A 的位置在树形机房的某个节点上,他想要管理在他所站节点子树中的同学(不算自己)。每个位置都有一个管理容易值\(w_i\) ,他能管理到某个位置 ,当且仅当他们的距离不超过\(w_i\)。
定位一个根\(x\),现在就是求\(u\in S\),\(S\)是\(x\)的所有子树所有节点集合,询问多少个\(u\)满足这样一个条件
\[
d[u]-d[x]\le w[u]
\]
其中\(d[u]\)表示\(u\)到根的距离
变化一下就是
\[
d[x]\ge d[u]-w[u]
\]
现在对于确定的\(x\),问题就变成了查询在他子树内有多少满足条件的\(u\)。由于\(d[u]-w[u]\)仅和\(u\)有关,直接\(O(n)\)预处理出\(d[u]-w[u]\)。
考虑如何查询满足\(d[u]-w[u]\)的个数,我们开个桶\(c[]\)维护每个\(d[u]-w[u]\) 的满足条件\(u\)的个数,由于我们直接查询的是前缀和,所以树状数组动态维护前缀和。
考虑如何得到子树内的满足条件\(u\)的个数,由于我们对于每个点都要在线查询\(\sum_{i\le d[x]} c[i]\le d[x]\)来得到答案,所以显然不能清空树状数组然后再进去子树查询。
考虑这样一种做法,就是在处理当前节点子树前,先查询一遍答案,这部分答案是不要的,递归处理完子树的节点后,再查询一遍答案。将第二次查询到的答案减去第一次得到的答案就是我们要的答案。
复杂度\(O(n\log n)\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=2e5+5;
struct E{
int to,nx,w;
E(){to=nx=w=0;}
E(const int&a,const int&b,const int&c){to=a; nx=b; w=c;}
}e[maxn<<1];
ll d[maxn],sav[maxn<<1],cha[maxn];
int head[maxn],w[maxn],len,seg[maxn<<1],ans[maxn];
int n,cnt,t0;
inline void add(const int&fr,const int&to,const int&w,const int&f=1){
e[++cnt]=E(to,head[fr],w);
head[fr]=cnt;
if(f)add(to,fr,w,0);
}
inline void add(const int&pos,const int&tag){
for(register int t=pos;t<=len;t+=t&-t) seg[t]+=tag;
}
inline int q(const int&pos){
register int ret=0;
for(register int t=pos;t>0;t-=t&-t) ret+=seg[t];
return ret;
}
void dfs1(const int&now,const int&last){
for(register int t=head[now];t;t=e[t].nx)
if(e[t].to!=last)
d[e[t].to]=d[now]+e[t].w,dfs1(e[t].to,now);
sav[++t0]=d[now]; sav[++t0]=cha[now]=d[now]-w[now];
}
void dfs2(const int&now,const int&last){
ans[now]-=q(d[now]);
for(register int t=head[now];t;t=e[t].nx)
if(e[t].to!=last) dfs2(e[t].to,now);
ans[now]+=q(d[now]); add(cha[now],1);
}
int main(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
n=qr();
for(register int t=1;t<=n;++t) w[t]=qr();
for(register int t=2,t1,t2;t<=n;++t)
t1=qr(),t2=qr(),add(t,t1,t2);
dfs1(1,0);
sort(sav+1,sav+t0+1); len=unique(sav+1,sav+t0+1)-sav-1;
for(register int t=1;t<=n;++t)
d[t]=lower_bound(sav+1,sav+len+1,d[t])-sav,cha[t]=lower_bound(sav+1,sav+len+1,cha[t])-sav;
dfs2(1,0);
for(register int t=1;t<=n;++t) printf("%d\n",ans[t]);
return 0;
}
原文地址:https://www.cnblogs.com/winlere/p/11437490.html