8.2
A.
考虑对每个二进制进行构造,直接给出构造方法:
while(c){ ++n; link(n,1); if(n>2) link(n-1,n); if(c&1) link(1,n); c>>=1; }
容易发现,这样可以让每一个二进制位满足条件。
B.
首先对所有操作的字符串建出trie树。
由于字符串总长度不大,所以可以暴力跳对应节点的祖先链来更新信息。
只要记录子树总大小,叶子个数,只有一个儿子的节点个数即可简单转移。
C.
首先考虑序列上怎么做,如果每个方案的权值是1那么可以简单容斥得到合法的方案数。
方案带权,那么考虑计算每一个划分方案的方案数,同样运用上面的容斥即可。
那么经过简单处理可以处理出每个颜色对应的EGF,卷起来即可得到合法的总权值。
考虑怎么处理环。
首先可以钦定第一个连续段一定是1,并且最后一个连续段一定不是1,并且给最后的答案乘上总个数.
只需要用开头是1的方案数减去开头结尾都是1的方案数即可。
发现钦定开头是1等价于给1的段数减少1,即EGF上对应的下标减少1.
于是可以计算出最后的方案数。发现这样的话会出一些小问题,就是一个包含k个连续1段的方案会被计算k次,于是在1的生成函数上继续做一些修改即可。
8.3
A. 欢迎来到塞莱斯特山 题
要统计每个点贡献可以想到实际上是合并子树内形成的若干段,合并的段数就是这个点被计算的次数。
于是可以想到一个 (dp:dp[i][j]) 表示 (i) 子树内形成的总段数为(j),段之间有序的贡献总和。
转移考虑子树归并,暴力枚举之前子树中的段数和当前字数中的段数和合并后的段数,那么发现需要求出合并的方案数,可以通过简单预处理得到。
(O(n^3))
B. 感受清风
发现每一列是独立的,于是可以分开考虑。
对每一列建一棵线段树,修改可以直接修改,查询可以直接在线段树上二分,考虑最后两种操作怎么维护。
由于矩阵是对称的,所以可以只考虑一种操作,另一种直接翻转坐标系即可。
发现总的修改次数是线性的,所以修改的时候可以暴力将上次吹风到现在所有操作撤销,然后暴力重新做一遍所有的修改。
(O(n log n))
C. 我的朋友们
首先有一个 (O(n^2)) 的暴力 (dp)。发现要靠数据结构优化不是很可行,而且似乎也没有什么其他的方法,所以大概只能考虑用多项式优化了?
考虑用分治(FFT)来优化这个东西。
令:
(p_i(x)=1-p[i]+p[i]*x) (S) 为p的前缀积。
(F) 为答案的生成函数。
(G_{l,r}(x)=S_l/S_{r-L})
(f_{l,r}(x)=F_{l-1}*G[l][r])
那么当 (l=r) 时,就可以用 (f_{l,r}(x)) 的第 (l) 项来统计答案。
首先 (G_{1,n}) 可以通过多项式求逆得到,(f_{1,n}=0),考虑利用当前区间的信息向下分治。
分治过程中要注意考虑变化量来计算,因为变化量的项数是区间长度级别的。
容易发现 ([l,r]) 区间的多项式最终只会更新 ([l,r]) 区间的答案,这提示我们不需要保留整个多项式,只需要保留有用的项。
可以发现 (f_{l,r}) 最终增加的次数一定不会超过 (r-l),于是 (G_{l,r}) 就只需要保留 (r-l) 项。
同理,由于增加的次数只有 (r-l) ,那么 (f_{l,r}) 小于 (l-(r-l)) 的项最终一定不会对答案产生影响。
所以最终每个区间的多项式项数都是 (O(r-l)) 级别的,总复杂度 (O(n log^2 n)).