题意:
有一棵
n
n
n个点的无根树,每个点有一个点权。有
m
m
m次询问,每次询问有两种,第一种是给你树上两个点
x
,
y
x,y
x,y,询问这条路径上不同颜色的个数;第二种是给你树上两个点
x
,
y
x,y
x,y,询问这条链上任取两点形成的子链不同的权值的点的个数之和。
这个题还有一个特别之处,就是他会先给你一条链,然后其他点是随机插入到已有的树上的。
n
<
=
1
e
5
,
m
<
=
2
e
5
,
10
s
n<=1e5,m<=2e5,10s
n<=1e5,m<=2e5,10s
题解:
我高一时二轮当场爆零的题目。
这题是我胡乱口胡的,网上好像也没有类似的想法,并且我没写代码,所以请各位谨慎参考。(担心正确性建议跳走)。
首先考虑到除了主链之外,其他点随机插入的话,其他点到主链的深度期望是 l o g log log的,所以朴素的想法是维护主链的信息,然后暴力处理这些插入的点。
为了处理主链,首先考虑是一条链的情况,这时就转化成了区间问题。我们暂时先不处理那些不在主链上的点。
首先考虑操作1,操作1其实就是统计区间本质不同的权值个数,详见HH的项链,做法众多。
然后扩展到树上的话,可以分为两部分,第一部分是这两个点跳到主链,第二部分是主链上的一段。如果跳不到主链直接暴力处理就行了,反正是 l o g log log的。主链上的可以主席树做。不在主链上的那一部分,我们先预处理,以序列的1号点为根dfs一遍,dfs过程中记录每个权值上次出现的深度。然后遍历这两段时先看是不是深度范围在这个点到主链深度浅的那段里,如果不在就答案加 1 1 1,并且记下这次询问有了这个颜色了,在另一条链时不再重复加这个颜色了。说的可能不太清楚,反正能做就是了。
当然操作1也可以只用莫队来做。
然后考虑操作2。同样先考虑一条链的情况。
我们把询问离线,每个询问找到在主链上的那一段,根据这个顺序来莫队。首先考虑在序列上莫队是情况。无非是移动左端点和右端点对答案会产生什么影响,下面来具体说明这个影响。
左端点:
向左:
考虑以当前扩展到的点为左端点的区间的答案之和,用这个来更新答案。我们维护每种权值在左侧第一次出现的位置,从这个位置开始向右到当前区间右端点的每一个位置都可以为这个颜色贡献
1
1
1的答案,即以扩展点为左端点,以这些点为右端点,这个颜色都有贡献。当然要先更新当前出现的这个颜色,更新方法是把这个颜色之前的那个值减去,然后加上新的贡献,即当前区间长度。
向右:
是唯一要减去答案的情况。和上面差不多,但是先减去这个点的答案,然后再更新被减去点的那种权值的信息,即找到下一个在区间里的这个权值在哪(如果有的话),这个可以预处理出来。
右端点:
向右:统计出现了一个新的右端点,每种权值有多少合法的左端点,换句话说,就是对于每种权值,这种权值最靠近这个右端点的第一个出现位置之前的每一个点都可以作为一个左端点,让这个颜色产生
1
1
1的贡献。那么我们只需要统计一下当前区间所有出现过的颜色的这个的和,累加一下。还是先更新当前点这个颜色,再统计答案。
显然莫队的右端点是没有向左的操作的。
还要考虑一个问题,就是左右端点移动时另一个端点若要移动,那要的信息还能不能维护了。
移动右端点时,对于左移左端点,相当于每种出现的颜色可行右端点数都加了 1 1 1,记录一下颜色个数就可以知道要增加多少。对于右移左端点的影响和左移是差不多的。
然后是移动左端点对移动右端点统计答案的影响。还是差不多的维护一个出现的颜色数。左移就相当于所有出现的颜色多了一个合法的左端点,右移就相当于所有出现的颜色少了一个合法的左端点。
可能还有一些别的细节,但是我觉得大体还是都能维护的。
然后再考虑如何上树。其实上树也不难,把询问的链拿出来之后,再把序列左右端点左移右移,但是不是在主链上移了,变为到树链上移,这个暴力移动是 l o g log log的,并且和莫队是根号是相加关系。统计出答案之后再移动回去就行。当然,这里还多了一个右端点的左移,但是维护的东西和左端点右移差不多。
这样这个题应该就做完了。复杂度 O ( n n + n l o g n ) O(n\sqrt{n}+nlogn) O(nn+nlogn)。
同样无代码。