P8868 [NOIP2022] 比赛

发布时间 2023-10-29 21:33:47作者: Zlc晨鑫

传送门


我们容易想到预处理区间 \([l, r]\) 中的 \(m_a \times m_b\)

这样算出来的是一个二维的矩阵,每次的答案就是红色部分:

但是这样的问题是二维的,无论如何都不是正解。

考虑把列这一维压掉,也就是令 \(w'_i \leftarrow w_{i,i} + w_{i,i+1} + ... + w_{i,r}\)

这样询问的答案就是一个序列求和了,就可能可以用线段树啥的来维护。

但是你发现线段树查询时能够约束的条件只有一维:\(L\),所以对于\(R\),我们需要通过枚举的顺序来保证 \(R\) 的限制,一个直观的做法将询问离线,按照 \(R\) 排序。

每次加入一个新的 \(R\),会多出一个 \(w_{i,R}\)。更新 \(w'\)的时候发现从下面算到上面,就能够\(O(n^2+qn)\)求解此问题了。

于是拿下\(20 pts\)

CODE


发现\(w'\)都是加上一个数,这就提示我们寻找哪些区间具体加上了哪些数。

于是你直接输出每次的 \(m_a\) ,打表找规律。

找那种规律明显的,比如29 29 29 29 29 30 ...29就是a[R]

非常明显了:一个数左边第一个大于等于它的数为a[pa[R]],那么在\([pa[R]+1, R]\)\(m_a\)都是\(a[R]\)

在此之前的 \(m_a\) 就还是原来的数,不变。

这个位置可以单调栈求解。

这时候我们就发现有些复杂了,因为有a和b,pa[R]pb[R]不一定相等。这时候我们一般先考虑一个序列,再考虑加上另一个序列的影响。比如这里先假设b的最大值没有变化。

题目直接需要维护的信息是\(w'\)的和,记为\(sw\)

\([pa[R]+1, R]\)的情况中,如果一个区间完全被包含,\(sw \leftarrow a[R] \times m_b[R] + a[R] \times m_b[R-1] + ...\),记\(smb\)为区间中所有\(i\)\([i,r]\)\(m_b\)的和,那么\(sw \leftarrow sw + smb \times k\)

\([1, pa[R]]\)的情况中,最大值没有变化,我们多加的那一部分,其实就是枚举到\(R-1\)时多加的那一部分,记为\(xy\),全部\(xy\)的和\(sxy\)\(sw \leftarrow sw + sxy\)

现在来考虑b的影响,在\([1, pb[R]]\),b的最大值没变化,相当于没有影响。
\([pb[R]+1, R]\),b的最大值都是\(b[R]\),消去在\([pa[R]+1, R]\)的情况中的影响,然后加上新的贡献。
\(sw \leftarrow sw - sxy + sma \times b[R]\)

可以先模拟一下信息的维护,防止推导出错。

CODE

然后你发现要维护

\[\begin{bmatrix} sw & sma & smb & sxy & len \end{bmatrix} \]

求出对应的系数矩阵就行了。

CODE

常数太大挂掉了……

这个时候我们就要优化矩阵乘法,详见这里

需要注意,不是说标记矩阵最初某个位置上是常量,它就一定是常量。

这个时候就是把所有操作统一起来,看下哪些位置上是变量。

然后写个程序,将常量(所有操作最初的系数矩阵中的常量)赋值为它对应的数,变量赋值为随机数,然后多乘几次,如果某个位置上的值一直不变,就可以认为它是常量了。

当然你也可以每一个位置去手算,但是这样太慢了。

箭头指出来的地方本来是常量,但是乘几次之后就不是了……

给它们标好序号,也就是\(v_0,...\)

每次算的时候一行行算,把每一个列对应抄出来,然后对应乘。

比如算\(v_3\)

\(v_4\)的时候,把列擦掉,行不用变,然后一样去算,这样正确率比较高。

信息矩阵一样的方法优化。