沈阳网络赛J-Ka Chang【分块】【树状数组】【dfs序】
Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero point.
Then, you need to handle QQ operations. There're two types:
1\ L\ X1 L X: Increase points by XX of all nodes whose depth equals LL ( the depth of the root is zero ). (x \leq 10^8)(x≤108)
2\ X2 X: Output sum of all points in the subtree whose root is XX.
Input
Just one case.
The first lines contain two integer, N,QN,Q. (N \leq 10^5, Q \leq 10^5)(N≤105,Q≤105).
The next n-1n−1 lines: Each line has two integer aa,bb, means that node aa is the father of node bb. It's guaranteed that the input data forms a rooted tree and node 11 is the root of it.
The next QQ lines are queries.
Output
For each query 22, you should output a number means answer.
样例输入
3 3 1 2 2 3 1 1 1 2 1 2 3
样例输出
1 0
题目来源
题意:
有一棵树 两种操作
一种是 将深度为i的所有节点的值加x
一种是 求以x为根的子树的所有节点之和
题解:
比赛的时候想到线段树 + dfs序了
也想到了将深度相同的节点用数组存
但是后面担心没办法维护修改 就有点不太会了
今天看题解的时候看到有一个人说 “ 一看这种没有办法直接用数据结构解决得问题就要考虑分块。”【学到了学到了】
对于同一深度设一个阈值limit,若这一深度中结点的个数>limit,则不实际进行更改,而是用一个数组来标记,因为>limit的层数不会太多,所以可以直接暴力枚举所有超越阈值的层数对于答案的贡献,同时二分答案即可。
节点数>block的为第一类,节点数<=为第二类。
对于第二类,每次修改操作我们暴力修改每个结点的影响值,因为涉及线段树或者树状数组的操作,时间复杂度为O(q*block*logn)。而每次通过线段树查询都是logn的
对于第一类,当修改的时候直接记录这一层被增加了多少,O(1)修改,然后查询的时候只需要枚举第二类的每一层,然后以这个结点为根节点的子树中属于这一层的节点数*这一层增加的值。这里的时间复杂度是O(q*n/block)
我们需要预处理出每个结点的子树中属于第一类层的节点数各有多少。这里我用的办法就是直接暴力。枚举每个点,如果它所在的层是第一类,那么更新它所有的父节点。这里的时间复杂度很容易被认为是O(n^2)。但是我们仔细分析一下发现它远远小于O(n^2)。因为最多有n/block层,所以这里的时间复杂度是O(n*n/block)
先不考虑预处理,只看操作的时间复杂度是O(q*block*logn+q*n/block).根据均值不等式最小是O(q*2*sqrt(nlogn)),当且仅当block取sqrt(n/logn)。这时候预处理的时间复杂度是O(n*sqrt(n*logn))经过计算时可以承受的(因为只有单组数据)。
1 // ConsoleApplication3.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 2 // 3 4 //#include "pch.h" 5 #include <iostream> 6 #include<algorithm> 7 #include<stdio.h> 8 #include<set> 9 #include<cmath> 10 #include<cstring> 11 #include<map> 12 #include<vector> 13 #include<queue> 14 #include<stack> 15 16 #define inf 0x3f3f3f3f 17 18 using namespace std; 19 20 typedef long long LL; 21 22 23 const int maxn = 1e5 + 5; 24 int n, q; 25 struct edge{ 26 int v, nxt; 27 }e[maxn << 1]; 28 int head[maxn], cnt; 29 int cntid, st[maxn], ed[maxn]; 30 vector<int>dep[maxn]; 31 vector<int>large; 32 LL sum[maxn], num[maxn]; 33 34 void init() 35 { 36 memset(head, -1, sizeof(head)); 37 memset(sum, 0, sizeof(sum)); 38 memset(num, 0, sizeof(num)); 39 cnt = 0; 40 cntid = 0; 41 } 42 43 void addedge(int u, int v) 44 { 45 e[cnt].v = v; 46 e[cnt].nxt = head[u]; 47 head[u] = cnt++; 48 } 49 50 void dfs(int u, int fa, int depth) 51 { 52 st[u] = ++cntid; 53 dep[depth].push_back(cntid); 54 for(int i = head[u]; i != -1; i = e[i].nxt){ 55 int v = e[i].v; 56 if(v != fa){ 57 dfs(v, u, depth + 1); 58 } 59 } 60 ed[u] = cntid; 61 } 62 63 void add(int pos, LL x) 64 { 65 while(pos <= n){ 66 sum[pos] += x; 67 pos += (pos & -pos); 68 } 69 } 70 71 LL query(int pos) 72 { 73 LL ans = 0; 74 while(pos){ 75 ans += sum[pos]; 76 pos -= (pos & -pos); 77 } 78 return ans; 79 } 80 81 int main() 82 { 83 init(); 84 scanf("%d%d", &n, &q); 85 for(int i = 1; i < n; i++){ 86 int u, v; 87 scanf("%d%d", &u, &v); 88 addedge(u, v); 89 addedge(v, u); 90 } 91 dfs(1, 0, 0); 92 //cout<<"y"<<endl; 93 int block = ceil(sqrt(n)); 94 for(int i = 0; i < n; i++){//节点大于阈值的层 95 if(dep[i].size() > block){ 96 large.push_back(i); 97 } 98 } 99 100 while(q--){ 101 int op; 102 scanf("%d", &op); 103 if(op == 1){ 104 int d; 105 LL x; 106 scanf("%d%lld", &d, &x); 107 if(dep[d].size() > block){ 108 num[d] += x; 109 } 110 else{ 111 for(int i = 0; i < dep[d].size(); i++){ 112 add(dep[d][i], x);//暴力修改 113 } 114 } 115 } 116 else{ 117 int x; 118 scanf("%d", &x); 119 LL ans = query(ed[x]) - query(st[x] - 1);//查询子树中属于第二类的节点的部分 120 for(int i = 0; i < large.size(); i++){ 121 //查询所有第一类的层数中 属于这棵子树的节点 乘以盖层的修改值 122 ans += (upper_bound(dep[large[i]].begin(), dep[large[i]].end(), ed[x]) - lower_bound(dep[large[i]].begin(), dep[large[i]].end(), st[x])) * num[large[i]]; 123 } 124 printf("%lld\n", ans); 125 } 126 } 127 128 return 0; 129 }