标签:树链剖分
描述
分析
- 刚拿到这个题时看到可以更改信仰的宗教, 也就是可以改变路径, 以为是动态树的题目(动态树不会), 后来发现都用的树链剖分, 为每个宗教建立一个线段树. 表示很神奇.
- 宗教数 <= 10^5, 每个宗教建立一个线段树, 线段树又一般开到四倍空间… 按我平常写线段树的方法肯定不行了. 于是从 HZWER 的博客里学到了动态的线段树, 需要访问某结点时给它分配编号, 记录结点的左右子结点编号而不是用
o<<1
表示o的左结点,o<<1^1
表示o的右结点的方法. 有点指针的感觉.- 一开始我卡在不知道怎么修改信仰宗教上, 看 HZWER 发现很简单, 把结点在最初信仰的宗教的线段树里的值修改为0, 再在改变后的信仰宗教的线段树里把值修改为原来的值.
- 这道题我调了很长时间, 直到最后发现一个最关键的错误出在查询上.
以查询和为例, 我一开始是这么写的 :
int ask_sum(int x, int y) { int ret = 0; while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); // top ret += query_sum(roots[c[x]], 1, n, tid[top[x]], tid[x]); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); ret += query_sum(roots[c[x]], 1, n, tid[x], tid[y]); return ret; }
- 这里c[x]表示x城市信仰的宗教, 当x改变时, c[x]势必会改变, 但我们要查找的线段树始终是不变的. 所以要记录x最初的线段树的根结点, 而不能每次都用
roots[c[x]]
来获取.
改后 :
int ask_sum(int x, int y) { int ret = 0, cx = c[x]; // here while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); // top ret += query_sum(roots[cx], 1, n, tid[top[x]], tid[x]); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); ret += query_sum(roots[cx], 1, n, tid[x], tid[y]); return ret; }
- 发现vector存图真的没邻接表好, 既费时间又费空间, 所以以后改用数组模拟邻接表来存了.
代码
标签:树链剖分
原文地址:http://blog.csdn.net/qq_21110267/article/details/44044905