【题解】[CJOI2019] 树上查询(树状数组)

【题解】[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

时间: 2024-10-04 22:40:07

【题解】[CJOI2019] 树上查询(树状数组)的相关文章

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

PAT甲题题解-1057. Stack (30)-树状数组

不懂树状数组的童鞋,正好可以通过这道题学习一下树状数组~~百度有很多教程的,我就不赘述了 题意:有三种操作,分别是1.Push key:将key压入stack2.Pop:将栈顶元素取出栈3.PeekMedian:返回stack中第(n+1)/2个小的数 建立一个栈来模拟push和pop,另外还需要树状数组,来统计栈中<=某个数的总个数不了解树状数组的建议学习一下,很有用的.树状数组为c,有个虚拟的a数组,a[i]表示i出现的次数sum(i)就是统计a[1]~a[i]的和,即1~i出现的次数当我要

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

Codeforces 570D TREE REQUESTS dfs序+树状数组

链接 题解链接:点击打开链接 题意: 给定n个点的树,m个询问 下面n-1个数给出每个点的父节点,1是root 每个点有一个字母 下面n个小写字母给出每个点的字母. 下面m行给出询问: 询问形如 (u, deep) 问u点的子树中,距离根的深度为deep的所有点的字母能否在任意排列后组成回文串,能输出Yes. 思路:dfs序,给点重新标号,dfs进入u点的时间戳记为l[u], 离开的时间戳记为r[u], 这样对于某个点u,他的子树节点对应区间都在区间 [l[u], r[u]]内. 把距离根深度相

BZOJ1878 SDOI2009 HH的项链 树状数组

题意:给定一个颜色序列,每组询问给出区间[l,r],求[l,r]中不同颜色的数量 题解: 首先把所有颜色离散化,然后离线,将询问按右区间升序排列.从1-N把整个序列扫一遍,设Pos[i]为第i个颜色最后出现的位置,假定当前扫到的位置为i,则更新Pos[a[i]],那么问题变成了:求一个序列(Pos)中,大于等于一个数(L)的数的数量. 用树状数组维护Pos=j的数的数量,每次查询树状数组中L-N的和即可. 貌似SDOI不喜欢考大型数据结构啊……坐等今年打脸 #include <cstdio>

POJ 2155 Matrix(树状数组+容斥原理)

[题目链接] http://poj.org/problem?id=2155 [题目大意] 要求维护两个操作,矩阵翻转和单点查询 [题解] 树状数组可以处理前缀和问题,前缀之间进行容斥即可得到答案. [代码] #include <cstring> #include <cstdio> const int N=1010; using namespace std; int c[N][N],n,m,T; char op[2]; void add(int x,int y){ int i,j,k

Codeforces Round #225 (Div. 1) C. Propagating tree dfs序+树状数组

C. Propagating tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/383/problem/C Description Iahub likes trees very much. Recently he discovered an interesting tree named propagating tree. The tree consists of n nodes numb

HDU 4417 Super Mario 划分树/树状数组

题目大意:给定一个序列,求区间内小于等于某数的元素数量 首先区间排名一看就是划分树 不过和第k小不一样 我们需要做一些处理 第一种处理方式是二分答案 然后转换成区间第k小 好方法我喜欢 但是这里说的不是这种方法 首先建树,然后对于每个询问,我们讨论k与a[mid]的关系 若k<a[mid],则右子树一定没有小于等于k的数,我们进入左子树查找 若k>=a[mid],则左子树内一定所有数都小于等于k,于是我们将查询区间中进入左子树的元素的数量记入ans,然后查找右区间 递归退出条件是查询区间为空或

树状数组详解(图形学算法)

目录 一.从图形学算法说起 1.Median Filter 概述 2.r pixel-Median Filter 算法 3.一维模型 4.数据结构的设计 5.树状数组华丽登场 二.细说树状数组 1.树 or 数组? 2.结点的含义 3.求和操作 4.更新操作 5.lowbit函数O(1)实现 6.小结 三.树状数组的经典模型 1.PUIQ模型 2.IUPQ模型 3.逆序模型 4.二分模型 5.再说Median Filter 6.多维树状数组模型 四.树状数组题集整理 一.从图形学算法说起 1.M