究极大水题,一眼背包板子,然后就做完了。
直接考虑树形 d p dp dp,令 f x , 0 f_{x, 0} fx,0 表示 x x x 的子树且不选 x x x 的答案, f x , 1 f_{x, 1} fx,1 表示 x x x 的子树且选 x x x 的答案,那么:
f
x
,
1
=
∏
y
∈
s
o
n
(
x
)
f
y
,
0
f
x
,
0
=
∏
y
∈
s
o
n
(
x
)
max
{
f
y
,
1
,
f
y
,
0
}
然后发现如果这样搞的话需要边取模边比大小,那么正确性就不能保证,所以要整个乘完之后再取模。但是这样显然就会爆 l o n g l o n g long \; long longlong,甚至会爆 __ i n t 128 int128 int128,所以我们要想想解决方案。
首先考虑高精,算了一下发现存不下,空间爆炸了。
然后考虑记录一个数被取模的次数,然后发现只能比较到 1 e 36 1e36 1e36 就是极限了,但是我们的数据会到达 1 e ( 1.8 e 6 ) 1e(1.8e6) 1e(1.8e6),所以还是不行。
于是这里就要用到一种比较聪明的做法,就是对点权取对数,这样要算 x y xy xy 就可以转变成 log x + log y \log x + \log y logx+logy,这样就不用担心爆 l o n g l o n g long \; long longlong 的问题了。
然后就做完了。
最暴力的暴力,按照题目描述模拟就好了。
题解上说的 25 p t s 25pts 25pts 是 O ( n 2 log n ) O(n^2 \log n) O(n2logn) 的做法,但是这里我有 O ( n 2 ) O(n^2) O(n2) 的做法qwq。
还是沿用暴力的思路,枚举区间长度然后判断这个区间长度可不可行。纯暴力的话判断区间长度是否可行的复杂度就是 O ( n 2 ) O(n^2) O(n2) 的,所以我们考了优化这个算法。
我们发现,在暴力算法中我们在维护的一个东西就很想某一个算法的板子,没错,就是莫队。我们就在维护每一种数字的数量(弄一个桶),所以我们现在也考虑 O ( 1 ) O(1) O(1) 移动区间端点。
记录一个 c n t cnt cnt 表示当前不满足条件的数字的个数,然后桶在移动端点的时候显然是可以 O ( 1 ) O(1) O(1) 计算的,那么我们考虑对于一个端点产生的贡献,分三种情况:
于是我们可以暴力扫一遍每一个长度的第一个区间,然后 O ( 1 ) O(1) O(1) 向后扩展,并判断 c n t cnt cnt 是否等于 0 0 0。这个做法复杂度就是 O ( n 2 ) O(n^2) O(n2) 的了。
首先 b b b 是单调不增的,也就是说大区间的约束没有小区间的严格,所以我们考虑从大区间加一些限制转到小区间。
我们发现,如果一个大区间中的某一个数字 p p p 会使得这个区间不合法,那么所有包含这个数字 p p p 的小区间都是不合法的,所以我们考虑找出大区间中所有这样的 p p p,并把这些点删掉,然后大区间就变成了许多的小区间。这样就可以分治处理。
但是这样的复杂度就有一点玄学了(据说是 O ( n n ) O(n \sqrt n) O(nn) 的),随便搞一搞就能知道应该过不了 1 e 6 1e6 1e6,所以还要继续优化。
首先每一次都找出出现次数不满足限制的所有数字这就是 O ( n ) O(n) O(n) 的,所以我们考虑每次只找到一个点然后一这个点为界拆分成两个区间,然后递归处理,这样不会影响正确性但是由于随便找一个点的位置是不均等的,所以时间会可能被卡成 O ( n 2 ) O(n^2) O(n2),所以我们就要用到启发式合并的思想。
想象递归向上合并,我们每次取小的合并,复杂度就一定是 O ( n log n ) O(n \log n) O(nlogn) 的,所以我们也沿用这种思路,看看能不能做到每次找到点 p p p 并分成两段之后只用 O ( 较小段 ) O(较小段) O(较小段) 的时间处理问题。
于是我们考虑当前区间为 [ l , r ] [l, r] [l,r] 并且我们拥有当前区间的桶,然后考虑怎样处理,处理的步骤如下:
这样一来,总体的时间复杂度就和启发式合并是相同的了,那么这道题就愉快的切掉了。