hdu_4918_Query on the subtree(树的分治+树状数组)

题目链接:hdu_4918_Query on the subtree

题意:

给出一颗n个点的树,每个点有一个权值,有两种操作,一种是将某个点的权值修改为v,另一种是查询距离点u不超过d的点的权值和。

题解:

这里可以去膜膜鸟神的博客

简单来说就是对树的每个重心建立两个树状数组,然后对于每个点修改就在每个重心的BIT中去修改,查询也在每个重心的BIT中查询,然后容斥一下,就得出答案。

每种操作的复杂度为log2n,这题的细节比较多,具体看代码。

  1 #include<bits/stdc++.h>
  2 #define F(i,a,b) for(int i=a;i<=b;i++)
  3 #define pb push_back
  4 using namespace std;
  5 const int N=1e5+7;
  6
  7 int n,q,g[N],nxt[N*2],v[N*2],ed,w[N],vis[N],id[N];
  8 int pool[40*N],C_ed,pool_ed,sz[N],mi,mx[N],ROOT;
  9 char op[2];
 10
 11 struct node
 12 {
 13     int rt,subrt,dis;
 14     node(){}
 15     node(int _rt,int _subrt,int _dis):rt(_rt),subrt(_subrt),dis(_dis){}
 16 }tmp;
 17 vector<node>vt[N];
 18
 19 void adg(int x,int y){v[++ed]=y,nxt[ed]=g[x],g[x]=ed;}
 20 void init(){ed=C_ed=pool_ed=0;F(i,1,n)vt[i].clear(),vis[i]=g[i]=0;}
 21 inline void up(int &a,int b){if(a<b)a=b;}
 22
 23 struct BIT
 24 {
 25     int *C,n;
 26     void init(int tot){n=tot,C=pool+pool_ed,pool_ed+=tot+1;F(i,0,n)C[i]=0;}
 27     inline void add(int x,int c){while(x<=n)C[x]+=c,x+=x&-x;}
 28     inline int ask(int x,int an=0)
 29     {
 30         if(x>n)x=n;
 31         while(x>0)an+=C[x],x-=x&-x;
 32         return an;
 33     }
 34 }tr[N*2];
 35
 36 void get_rt(int u,int fa,int num)
 37 {
 38     sz[u]=1,mx[u]=0;
 39     for(int i=g[u];i;i=nxt[i])
 40         if(!vis[v[i]]&&v[i]!=fa)
 41         {
 42             get_rt(v[i],u,num);
 43             sz[u]+=sz[v[i]],up(mx[u],sz[v[i]]);
 44         }
 45     up(mx[u],num-sz[u]);
 46     if(mx[u]<mi)ROOT=u,mi=mx[u];
 47 }
 48
 49 void del(int u,int fa,int rt,int subrt,int dis=1)//将子树的每个点放进对应的重心
 50 {
 51     vt[u].pb(node(rt,subrt,dis));
 52     tr[rt].add(dis+1,w[u]);
 53     tr[subrt].add(dis+1,w[u]);
 54     for(int i=g[u];i;i=nxt[i])
 55         if(v[i]!=fa&&!vis[v[i]])
 56             del(v[i],u,rt,subrt,dis+1);
 57 }
 58
 59 void init_tree(int u=1,int num=n)
 60 {
 61     mi=N,get_rt(u,u,num);
 62     int rt=ROOT,rt_id=++C_ed;
 63     tr[C_ed].init(sz[u]+2);
 64     vis[rt]=1,vt[rt].pb(node(C_ed,0,0));
 65     tr[C_ed].add(1,w[rt]);
 66     get_rt(rt,rt,num);//从新计算sz的大小
 67     for(int i=g[rt];i;i=nxt[i])
 68         if(!vis[v[i]])
 69         {
 70             tr[++C_ed].init(sz[v[i]]+2);
 71             del(v[i],v[i],rt_id,C_ed);
 72         }
 73     for(int i=g[rt];i;i=nxt[i])
 74         if(!vis[v[i]])
 75             init_tree(v[i],sz[v[i]]);
 76 }
 77
 78 int main()
 79 {
 80     while(~scanf("%d%d",&n,&q))
 81     {
 82         init();
 83         F(i,1,n)scanf("%d",w+i);
 84         F(i,1,n-1)
 85         {
 86             int x,y;
 87             scanf("%d%d",&x,&y);
 88             adg(x,y),adg(y,x);
 89         }
 90         init_tree();
 91         while(q--)
 92         {
 93             int u,v;
 94             scanf("%s%d%d",op,&u,&v);
 95             if(op[0]==‘!‘)
 96             {
 97                 int size=vt[u].size(),d=v-w[u];
 98                 F(i,0,size-1)
 99                 {
100                     tmp=vt[u][i];
101                     tr[tmp.rt].add(tmp.dis+1,d);//dis+1去掉距离为0在BIT上超时的BUG
102                     if(tmp.subrt)tr[tmp.subrt].add(tmp.dis+1,d);
103                 }
104                 w[u]+=d;
105             }else
106             {
107                 int d=v,ans=0,size=vt[u].size();
108                 F(i,0,size-1)
109                 {
110                     tmp=vt[u][i];
111                     ans+=tr[tmp.rt].ask(d-tmp.dis+1);
112                     if(tmp.subrt)ans-=tr[tmp.subrt].ask(d-tmp.dis+1);
113                 }
114                 printf("%d\n",ans);
115             }
116         }
117     }
118     return 0;
119 }

时间: 2024-10-29 19:06:01

hdu_4918_Query on the subtree(树的分治+树状数组)的相关文章

[bzoj3155]Preprefix sum(树状数组)

3155: Preprefix sum Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 1183  Solved: 546[Submit][Status][Discuss] Description Input 第一行给出两个整数N,M.分别表示序列长度和操作个数 接下来一行有N个数,即给定的序列a1,a2,....an 接下来M行,每行对应一个操作,格式见题目描述 Output 对于每个询问操作,输出一行,表示所询问的SSi的值. Sample In

HDU 1166 —— 敌兵布阵 【树状数组 or 线段树】

http://acm.hdu.edu.cn/showproblem.php?pid=1166 需求: 1.点修改 2.区间求和 标准的BIT(二叉索引树,又名树状数组)问题,当然也可以用最基础的仅支持“点修改”的线段树来解决! 线段树版本: #include <cstdio> #include <iostream> #define INF 0x3f3f3f3f using namespace std; const int MAXN = 65536 + 5; // 65536 是最小

二分索引树与线段树分析

二分索引树是一种树状数组,其全名为Binary Indexed Tree.二分索引树可以用作统计作用,用于计某段连续区间中的总和,并且允许我们动态变更区间中存储的值.二分索引树和线段树非常相似,二者都享有相同的O(log2(n))时间复杂度的更新操作和O(log2(n))时间复杂度的查询操作,区别在于二分索引树更加简洁高效,而线段树则较冗杂低效,原因在于对二分索引树的操作中是使用了计算机中整数存储的特性来进行加速,而线段树中由于使用的是比较操作,因此性能不及二分索引树.那么为什么我们不抛弃线段树

【HDU】4918 Query on the subtree 点分治+树状数组

传送门:[HDU]4918 Query on the subtree 题目分析: 首先,简化问题. 1.求一次到点u的距离不超过d的点的个数.很容易,一次O(NlogN)的点分治便可以完成. 2.多次进行操作1.此时不能每次都O(NlogN)了,太慢了.我们考虑到对于点分治,树的重心一共有logN层,第一层为整棵树的重心,第二层为第一层重心的子树的重心,以此类推,每次至少分成两个大小差不多的子树,所以一共有logN层.而且,对于一个点,他最多只属于logN个子树,也就是最多只属于logN个重心.

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

POJ 2299 Ultra-QuickSort (树状数组or 归并排序分治求逆序对数)

题目大意就是说帮你给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数(n<=500000) 显然不能直接模拟冒泡排序,其实交换的次数就是序列的逆序对数. 由于数据范围是 0 ≤ a[i] ≤ 999,999,999所以先要离散化,然后用合适的数据结果求出逆序 可以用线段树一步一步添加a[i],每添加前查询前面添加比它的大的有多少个就可以了. 也可用树状数组,由于树状数组求的是(1...x)的数量和所以每次添加前查询i-sum(a[i])即可 树状数组: //5620K 688MS #incl

BZOJ1176---[Balkan2007]Mokia (CDQ分治 + 树状数组)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1176 CDQ第一题,warush了好久.. CDQ分治推荐论文: 1 <从<Cash>谈一类分治算法的应用> 陈丹琦 2 <浅谈数据结构题的几个非经典解法>  许昊然 关于CDQ分治,两种要求:①操作不相互影响  ②可以离线处理 题目描述是有问题的,,初始时 全部为0,不是s 题意:二维平面内,两种操作,1 x y v ,位于(x,y)的值加上v...2 x1,

bzoj2716 [ Violet 3 ] --cdq分治+树状数组

树状数组打错调了一个小时... 对于点(x,y),其它点只会在他的左下.右下.左上.右上四个方向上.我们只需求在左下方向上就可以了,因为其他方向可以通过改变相对位置求得. 考虑cdq分治.先按x坐标排序,然后将区间[l,r]分为[l,mid],[mid+1,r],因为只求左下方向上的点,所以可以去掉绝对值:dis=x+y-(x'+y') 只需求x'+y'最大的点就可以了.求(X,Y)时将[l,mid]中x值小于X的点的x+y值在树状数组中更新,然后查询y小于Y的最大的x+y,更新答案. 代码:

【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.  T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互