1 条题解
-
0
自动搬运
来自洛谷,原作者为

Parabola
mua~mua~嘟嘟你的嘴搬运于
2025-08-24 21:34:24,当前版本为作者最后更新于2019-02-15 15:08:13,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Part0.题外话
总算搞出来这一道题了
从上午8:30做到下午2:30
饭都没吃
累死我了
不过想到明天能去买轻小说就敲开心这篇题解是一边做饭一边写出来的
另外,窝的做法是目前(2019/2/15)所有题解内中最优复杂度
不知道还有没有神仙能够更快
Part1.前置知识
分块、分块、还有分块
好吧其实还有一个并查集
Part2.BB了那么久总算开始了
首先这一题的所谓「树分块」的做法是错误的,可以被菊花图卡掉
所以要换种方法分块
回答一个询问,就是把初始答案与每一个操作带来的影响所整合起来
到这一题上,假设窝们对于初始那棵树(没执行过任何一次修改操作的)进行一次dfs算出dfs序后
树上问题就成了序列问题
那么以为根的子树所对应的区间就是
(dfn代表dfs序)
所以可以拿个数据结构来维护初始树
记
T1为这个数据结构的单次查询复杂度,T2为构造复杂度那接下来窝们只需要回答每一个修改操作对询问操作的影响了
第一个问题 操作1如何影响询问
操作1是一个点权修改
那它想影响一个询问,必须是修改这个子树里的点权
操作2是一个建一个新的子节点,他只需要在这棵子树里建的就会影响了
综上,我们现在得到的思路是这样的
先用一个数据结构维护初始状态,接着对于每一个询问,扫一次它前面所有的修改操作,如果发现能够影响答案,那就将答案改变
但这样复杂度是不对的,因为这和暴力没什么区别
要不我们每
T次操作之后,将初始状态提前一次?你可能没有理解这句话,就是说每
T次操作之后,我们把执行T次操作得到的新树作为新的初始状态换句话来说,每
T次操作,重新dfs+建数据结构一次这个时候你就发现,每一次询问往后面扫的不是
M次了,而是M/T次你很聪明知道
T应该取综上,我们要实现的操作只剩下两个了
1.写一个数据结构能够维护区间rank(就是区间有多少个比大)
2.判断一个点是否在一个子树中
看到1窝们就想到主席树,2的话因为有修改,我们可以用倍增
,但是这样不是最优的
瓶颈在于倍增与主席树的构造上
主席树构造时间复杂度是,一共要暴力构造次。时间复杂度
由于对于每一个询问操作我们都要往前扫个修改并且每个修改花的时间倍增,最后再加上一个主席树查询。
每一次询问的复杂度是
总复杂度
极大的复杂度不平衡。
为什么一定要倍增呢?
路径压缩难道忘了吗?
均摊复杂度分析难道忘了吗?
我们将倍增换成加了路径压缩的暴力,再配合一下欧拉序
均摊分析一下复杂度
不就成了了吗
再把主席树换成一个块内维护有序的分块,构造时间为
总复杂度为
还有许多小细节,可以看看代码
Part3. Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<vector> #include<cstring> using namespace std; const int MAXN = 60000 + 5; vector <int> G[MAXN]; int n , m , num , f[MAXN] , sz[MAXN] , val[MAXN] , dfn[MAXN] , tp[MAXN]; int real_node , all_node; namespace DFS{ int dfs_clock , st[MAXN] , ed[MAXN]; void dfs(int u , int fa) { sz[u] = 1 , f[u] = fa , dfn[u] = num++ , st[u] = ++dfs_clock; for(int i = 0 , v ; i < (int)G[u].size() ; ++i) { if((v = G[u][i]) == fa) continue; dfs(v , u); sz[u] += sz[v]; } ed[u] = ++dfs_clock; } inline bool check(int u , int v) { //节点v是否在以u为根的子树中 return st[u] <= st[v] && ed[v] <= ed[u]; } void rebuild() { num = dfs_clock = 0; dfs(1 , 0); } } namespace BLOCK{ int SIZE , j , cnt , A[MAXN] , b[MAXN] , ord[MAXN / 100][MAXN / 100]; void rebuild() { for(int i = 0 ; i < cnt ; ++i) memset(ord[i] , 0 , sizeof ord[i]); memset(ord[cnt] , 0 , sizeof ord[cnt]); SIZE = (int)sqrt(all_node + 0.5); j = 0 , cnt = 0; for(int i = 1 ; i <= all_node ; ++i) A[dfn[i]] = val[i]; for(int i = 0 ; i < all_node ; ++i) { if(j == SIZE) j = 0 , ++cnt; ord[cnt][j] = A[i] , b[i] = cnt; ++j; } for(int i = 0 ; i < cnt ; ++i) sort(ord[i] , ord[i] + SIZE); sort(ord[cnt] , ord[cnt] + j); } int query(int l , int r , int c) { int lb = b[l] , rb = b[r] , res = 0; if(lb == rb) { for(int i = l ; i <= r ; ++i) if(A[i] > c) ++res; return res; } for(int i = l ; i < (lb + 1) * SIZE ; ++i) if(A[i] > c) ++res; for(int i = rb * SIZE ; i <= r ; ++i) if(A[i] > c) ++res; for(int i = lb + 1 ; i < rb ; ++i) res += ord[i] + SIZE - upper_bound(ord[i] , ord[i] + SIZE , c); return res; } } struct Node{ int opt , u , x , lst; Node(int opt = 0 , int u = 0 , int x = 0 , int lst = 0) : opt(opt) , u(u) , x(x) , lst(lst) {} }; vector <Node> v; inline int real(int u) {return u <= real_node;} inline int Get(int v) { if(real(f[v])) return f[v]; return tp[v] = Get(f[v]); } inline bool check(int u , int v) { if(real(u) ^ real(v)) { if(real(v) == 1) return false; if(!tp[v]) tp[v] = Get(v); return DFS::check(u , tp[v]); } else { if(real(u) == 1) return DFS::check(u , v); while(!real(f[v])) { if(v == u) return true; v = f[v]; } return v == u; } } int main() { int last_ans = 0; scanf("%d" , &n); for(int i = 1 , u , vv ; i < n ; ++i) { scanf("%d %d" , &u , &vv); G[u].push_back(vv); G[vv].push_back(u); } for(int i = 1 ; i <= n ; ++i) scanf("%d" , val + i); real_node = all_node = n; DFS::rebuild(); BLOCK::rebuild(); scanf("%d" , &m);int SIZE = (int)sqrt(m + 0.5) + 1 , j = 0; while(m--) { int opt , u , x; scanf("%d %d %d" , &opt , &u , &x); if(j == SIZE) { memset(tp , 0 , sizeof tp); for(int i = 0 ; i < (int)v.size() ; ++i) if(v[i].opt == 2) G[v[i].lst].push_back(++real_node); DFS::rebuild(); BLOCK::rebuild(); v.clear(); j = 0; } u ^= last_ans , x ^= last_ans; if(opt == 0) { int ans = BLOCK::query(dfn[u] , dfn[u] + sz[u] - 1 , x); for(int i = 0 ; i < (int)v.size() ; ++i) { if(check(u , v[i].u)) { if(v[i].opt == 1) { if(v[i].lst > x && v[i].x <= x) --ans; if(v[i].lst <= x && v[i].x > x) ++ans; } if(v[i].opt == 2) ans += v[i].x > x; } } printf("%d\n" , last_ans = ans); } else if(opt == 1) { v.push_back(Node(opt , u , x , val[u])); val[u] = x; } else { v.push_back(Node(opt , ++all_node , x , u)); val[all_node] = x; f[all_node] = u; } ++j; } return 0; }
- 1
信息
- ID
- 1121
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者