数树数

题目大意及模型转换

给定一棵由N个结点组成的树,现在有两种操作。1、将第i个结点权值改为x。2、询问第i个结点到第j个结点路径上的点有多少个权值为x。N<=100000,操作数Q<=200000。一开始第i个结点权值为v[i]。

小感想

2103年的题目真是鬼。。。这标题都不知道是什么鬼。。。什么可修改主席树又难打又难跳我还不会!比赛果断使用树上分块大水(分块大法好!)。题解说常熟大,加上我想到的实现方法要进行检索。作为一个合格的双语言选手(请大家购买我的双语不用教!),我决定抛弃Pascal,让他一边玩去(Pascal跑得慢!)。重拾C++。

正解

我们可以发现,保存那些数并没有什么卵用。因为实际有用的是那些要询问的数。那么这样想,可以把所有询问操作最后的x提取出来,排个序来去个重。现在,有用的就是去重后的这个序列。我们可以尝试枚举这其中一个数,然后再去枚举所有操作。假设枚举的是b,我们可以用线段树维护每个点到根的路径上多少点的权值为b。那么对于修改操作,将a修改为b,等同于对整个子树的答案都加1。同理将b修改为a,也是将整个子树答案都减一。为了支持子树修改,我们处理出深搜序列与每个点为根子树的大小。那么更改以i为根的子树,就是修改区间dfn[i]…dfn[i]+size[i]-1。线段树支持区间修改即可。对于询问操作,如果最后的x=b,就可以求得答案。每次要将线段树清空。

模链优化

我们发现,按照这个规则,每个修改操作若是将原先权值a修改为b,只涉及a和b这两个量,对于不等于a,b其中一个的数c,枚举到c时,枚举这个修改操作没什么卵用。也就是说,每个修改操作最多被访问两次。因此我们采用模链(数组模拟链表)进行优化,使其复杂度降为O(2*Q log N)。那么对于每个修改操作,我们只需要检索将被枚举的序列中存不存在a,存不存在b,然后连边。注意,你需要保证访问顺序按照操作顺序进行。

检索优化

检索这个玩意,我习惯用红黑树(也就是c++可以直接调用的set或multiset)。也可以排序后用二分查找。将初始化复杂度降为O(Q log Q)。

不清空线段树

我们如何不清空线段树?如果清空线段树,时间依旧很大。我们可以在Q个操作前增加N个操作,第i个操作将第i个权值改为v[i]。(那么默认所有结点权值初始为0)。再在Q个操作后增加N个操作,第i+N+Q个操作将第i个权值改为0。这样,我们就当一共有N*2+Q操作,像上面说的做即可。

至于答案

询问x到y,找到z=lca(x,y),那么用father[z]表示z的父亲。答案为x的值+y的值-z的值-father[z]的值(值说的就是在线段树里维护的那个值)。

注意

数组要开好。。。不要爆了!

你可能感兴趣的:(数树数)