noi前第十九场 题解
A. 欢迎来到塞莱斯特山
容易想到对每个联通段进行 \(dp\)。
这样在归并子树的过程中只需要枚举两者分别的段数,枚举合并成多少段。
转移系数只要做一个 \(O(n^3)\) 的 \(dp\) 就可以预处理出来。
复杂度大概就是子树归并的复杂度 * \(n\),写个指针卡卡内存就能过了。
B. 感受清风
发现这样的事情,当一个箱子被吹到边界之后就会一直在边界。
对于在边界的箱子,不关注具体是谁,只关注个数。
如果箱子都在边界上,可以直接用线段树维护一下最小值,然后对询问二分找到第一个不合法的位置就好了。
对于存在加入删除操作,可以对线段树可持久化一下,对每一列维护一棵线段树。
每次操作只要单独修改这一列对应的线段树,并把这份贡献在风吹的时候修改到主线段树上就好了。
当前的箱子在左侧或者右侧并不重要,可以默认箱子在左侧。
当箱子在右侧的时候,对坐标翻转一下就好了。
C. 我的朋友们
可以发现问题就是求一个多项式,然后 \(dp\) 转移一下。
暴力做多项式乘法、多项式除法,可以做到 \(O(n^2)\)。
然后正解是这样做的,首先对原数组翻转一下,就可以从小到大方便转移。
设 \(P_i(x)=p_ix+(1-p_i)\),\(S_i(x)=\prod \limits_{j=1}^i P_j(x)\)。
\(F_i(x)=\sum \limits_{j=1}^i f_j x^j\),就是答案的生成函数。
\(G_i(x)=\prod \limits_{j=i-L+1}^i P_j(x)\),表示第 \(i\) 个点的转移系数。
那么有 \(f_i=[x^i]F_{i-1}(x)G_{i}(x)\)
进行分治,对于每个分治区间维护两个多项式:
\(G_{l,r}(x)=\frac{S_l(x)}{S_{r-L}(x)}\)
\(F_{l,r}(x)=F_{l-1}(x)G_{l,r}(x)\)
上面的 \(G_{l,r}(x)\) 维护的是整个 \([l,r]\) 区间共同包含的转移系数,\(F_{l,r}(x)\) 为共同的可行转移点。
容易发现当 \(l=r\) 时,\([x^l]F_{l,r}(x)\) 就是要求的答案。
考虑这两个多项式在分治过程中的转移,只要考虑变化量就可以写成只与区间内信息有关的形式。
然而暴力去做的复杂度仍然不对,可以考虑不去维护两个多项式的所有项。
观察可以发现,\(G\) 多项式只需要维护 \(r-l\) 项,因为它能提供的作用只是把左区间的 \(F\) 转移到右区间上。
\(F\) 多项式的超过 \(x^r\) 的项一定是没有必要的,因为即使递归到结尾也只需要 \([x^r]\) 项。
\(F\) 多项式的低于 \(x^{l-(r-l)}\) 的项也是没有必要的,因为最终要取得的系数是 \(x^l\) 到 \(x^r\),观察 \(F\) 的转移发现至多只能增加区间长度项。
这样压缩的话,\(F,G\) 多项式的长度均为区间长度,所以复杂度是 \(O(n \log^2 n)\) 的。