ZJOI2019

麻将(期望、DP套DP)

先考虑如何计算一个子集是否能胡。

\(f_{i,0/1,j,k}\)表示考虑了子集中\(1 \sim i\)的牌,是否找到对子,\(i-1,i,i+1\)预计拿\(j\)个,\(i,i+1,i+2\)预计拿\(k\)个,最多能够产生多少面子。注意到\(j\)\(k\)的状态都是预计,所以并不算入面子数。转移枚举有多少个\(i+1\)、预计拿多少个\(i+1,i+2,i+3\)的顺子,剩下的全部拿刻子。至于为什么考虑\(i+1,i+2,i+3\)而不是\(i-1,i,i+1\)是因为这样边界比较好算。

然后考虑原问题。发现原问题相当于:对于手中摸了\(i(i \geq 13)\)张牌,考虑这些牌有没有胡的子集,如果没有贡献+1,然后再摸一张。所以我们只需要计算满足摸了\(i\)张还没有胡的子集的方案数,然后乘上\((4n-i)!\)加起来就是所有排列的权值总和。

后面的问题仍然可以从小到大枚举牌来DP。考虑将上面\(f\)的状态压进当前DP状态里,设\(g_{i,j,k}\)表示考虑了前\(i\)种牌,共拿了\(j\)张牌,\(f\)数组的状态为\(k\)时的方案数,转移枚举\(i+1\)选了多少个,转移一下\(k\)即可。

最后的问题是\(k\)的状态量,暴搜可以搜出本质不同的不胡状态总共有\(2091\)种,所以复杂度为\(O(2091n^2)\)可以通过。

代码

线段树(期望、线段树、矩阵)

原题可以等价转化为:一棵线段树有\(0.5\)的几率打一个标记,有\(0.5\)的几率不打,问标记个数期望。

\(f_{i,0/1/2}\)表示第\(i\)个点,它和它的祖先都不存在标记/它的祖先存在标记,它不存在标记/它存在标记的期望。转移分:经过的点、pushdown到达的点、打上标记的点、祖先打上标记的点四种情况分别乘上一个矩阵转移,最后的答案就是所有的\(f_{i,2}\)的和。

可能需要一定的常数优化。可以发现\(f_0+f_1+f_2 = 1\),所以可以压掉一个变成\(2 \times 2\)

代码

Minimax搜索(动态DP)

对于一个已知的集合\(S\),如果通过\(w(S)\)将根节点权值变为\(W+x\),那也一定可以变为\(W+1\);能够变为\(W-x\),也一定能够变为\(W-1\)。所以我们只需要考虑将根节点变为\(W \pm 1\)的能量消耗。

首先,如果集合中存在\(W\)号点,那么它的权值一定为\(1\),因为只需把\(W\)修改成\(W \pm 1\)就可以改变根节点的值。所以若设\(ans_i\)表示\(k \leq i\)的答案的和,那么\(ans_1 = 2^{m-1}\),由题意可知\(ans_n = 2^m - 1\)。对于接下来考虑的所有集合,均认为它们不包含\(W\)号点。

我们枚举一个\(k \in [L,R]\)考虑计算\(ans_k\)。对于一个集合\(S\),如果想让根节点权值变为\(W+1\),那么编号\(\geq W+1\)的点的权值不需要变化,而\(<W\)的点的权值在改变量不超过\(k\)的情况下变为\(W+1\)一定是最优的;而变为\(W-1\),则只让\(>W\)的点在改变量不超过\(k\)的情况下把权值变为\(W-1\)。那么也就是说:一个集合中让根节点权值变为\(W+1\)需要修改的点和让根节点权值变为\(W-1\)需要修改的点是独立的,所以不满足条件的方案数就是:选择一些\(<W\)的点,将它们修改为\(W+1\)之后根节点权值不变;选择一些\(>W\)的点,将它们修改为\(W-1\)之后根节点权值不变,这两种方案的乘积。

那么可以设计一个\(dp\):设\(f_i\)表示仅改变编号\(<W\)的点的权值,点\(i\)的权值\(\leq W\)的概率。至于为什么是概率,因为后面好算。又设\(g_i\)表示仅改变编号\(>W\)的点的权值,点\(i\)的权值\(<W\)的概率。这两种dp的转移都是一样的,如果\(2 \not\mid dep_u\)\(f_u = \prod f_v\),否则是\(f_u = 1 - \prod (1 - f_v)\),其中\(v\)\(u\)的儿子。

那么\(ans_k = 2^{m-1} + 2^{m-1}(1 - f_1(1 - g_1))\)。对于每一个\(k \in [L-1,R]\)算一下答案,就有一个70pts的DP。

然后可以发现:当\(k\)\(i\)变到\(i+1\)的时候,至多有一个叶子会在\(f\)中改变值,至多有一个叶子在\(g\)中改变值,所以动态DP。

具体的,令\(f'_v = [2 \not\mid dep_v]f_v + [2 \mid dep_v](1 - f_v)\),那么\(f'\)的转移就变成了统一的\(f'_u = \prod (1 - f'_v)\)。然后设\(x^i\)表示树链剖分后\(x\)所在重链深度为\(dep_x + i\)的点,那么一段重链的转移可以写成\(f_{top} = \sum\limits_{i=0}^{dep} (-1)^i \prod\limits_{j=0}^i ldp_{top^j}\),其中\(ldp\)表示的是轻边传上来的答案,至于怎么推直接暴力拆式子。这个式子的值可以通过每条重链维护一棵线段树求。

最后动态DP撤销贡献的时候有可能会除\(0\),所以对于每一个数记一下非\(0\)的数的乘积和乘\(0\)次数,就可以做到除法。

代码

开关(生成函数)

\(P = \sum\limits_{i=1}^N p_i\)。设\(F(x)\)表示经过若干步到达终止状态的概率,由指数型生成函数有\(F(x) = \prod\limits_{i=1}^n (\frac{e^{\frac{p_ix}{P}}}{2} + (-1)^{s_i} \frac{e^{-\frac{p_ix}{P}}}{2})\)。在实际操作中可以记\(F(x) = \sum\limits_{i=-P}^P a_ie^{\frac{ix}{P}}\),这样可以使用背包求出\(F(x)\)

但这样显然会算重,因为可能已经存在一个前缀满足条件。那么前缀之后的操作做完了之后回到了操作前的状态。不妨设\(G(x)\)表示若干步操作做完后回到原来状态的概率,那么\(G(x) = \prod\limits_{i=1}^n (\frac{e^{\frac{p_ix}{P}}}{2} + \frac{e^{-\frac{p_ix}{P}}}{2})\)。将\(G(x)\)写成与\(F(x)\)一样的形式,即\(G(x) = \sum\limits_{i=-P}^P b_ie^{\frac{ix}{P}}\)

注意到上面两个多项式因为是指数型生成函数,所以系数不是真正的概率,还要乘下标的阶乘。而\(i![x^i]e^{ax} = a^i\),故\(\sum\limits_{i=0}^{+\infty} i![x^i]e^{ax} = \frac{1}{1 - a}\)。这样可以得到普通型生成函数意义下的\(F(x) = \sum\limits_{i=-P}^P \frac{a_i}{1 - \frac{ix}{P}}\),\(G(x) = \sum\limits_{i=-P}^P \frac{b_i}{1 - \frac{ix}{P}}\)

\(H(x) = \sum\limits_{i=0}^\infty h_i x^i\)表示长度为\(i\)且前缀不存在完成条件的概率的多项式,那么有\(H(x)G(x) = F(x)\),即\(H(x) = \frac{F(x)}{G(x)}\)

答案是\(\sum\limits_{i=1}^\infty h_ii\),也就是\(H'(1)\),使用\((\frac{F(x)}{G(x)})' = \frac{F'(x)G(x) - G'(x)F(x)}{G^2(x)}\)计算。但是注意到\(1 - \frac{P}{P} = 0\),也就是说\(F(1)\)\(G(1)\)没有意义。考虑在\(F(x)\)\(G(x)\)上同时乘\(\prod\limits_{i=-P}^P (1 - \frac{ix}{P})\),显然不会影响答案,有\(F(x) = \sum\limits_{i=-P}^P a_i \prod\limits_{j=-P}^P [i \neq j](1 - \frac{jx}{P})\)。则\(F(1) = a_P \prod\limits_{j=-P}^{P-1}(1 - \frac{jx}{P})\)

又有\((\prod\limits_{i} (a_ix + 1))' = \sum\limits_i a_i \prod\limits_{j \neq i} (a_jx + 1)\),证明可以考虑贡献\(x^k\)项系数的数,一定是\(k\)\(a_i\)的乘积,那么在右式中枚举到其中一个\(a_i\)就有一次贡献,就会产生\(k\)次贡献,与导数运算相符。代进去化简一下式子就可以得到\(F'(1) = -[\prod\limits_{i = -P}^{P-1} (1 - \frac{i}{P})](\sum\limits_{i=-P}^{P-1} \frac{a_i}{1 - \frac{i}{P}} + a_P \sum\limits_{i=-P}^{P-1} \frac{\frac{j}{p}}{1 - \frac{j}{p}})\)。那么我们就可以求出\(F(1),F'(1),G(1),G'(1)\),就可以得到答案。

注意到\(a_P = b_P = \frac{1}{2^n}\),所以将\(F(1),F'(1),G(1),G'(1)\)代进去之后可以化简掉很多东西,最后可以化简为\(2^n \sum\limits_{i=-P}^{P-1} \frac{b_i - a_i}{1 - \frac{i}{P}}\)

代码

语言(链并、线段树合并)

题目相当于对于每一个点,求以其为根时经过这个点的路径的链并的点数总和。这相当于对于每一个点求出以\(1\)为根时经过这个点的路径链并的点数,再减去点\(1\)到链并中所有点的LCA的距离。

对于每一条路径\((x,y)\)树上差分,在\(x\)\(y\)处的链并中插入这两个点,在\(fa[LCA(x,y)]\)处删掉这两个点。那么我们需要资瓷合并若干个儿子的链并,线段树合并做一下就可以了。

代码

浙江省选(半平面交、二分)

对于\(M=1\)的情况就是求一个半平面交,因为斜率都是正的所以就相当于求一个右下凸包。注意如果一条线段覆盖的横坐标范围内没有整点也是要被删掉的。

对于\(M=2\)的情况,我们求出能够排到第一名的人之后,对于其他人再求一个半平面交,那么一个人的最优名次为\(2\)的必要条件是他对应的直线在半平面交上。然后考虑第一名的人会覆盖到哪些区域,也就是在半平面交上二分一下每一条直线在横坐标的哪一个范围内会在半平面交以上,即覆盖掉当前的半平面交,那么一个人最优名次为\(2\)的充要条件就是他对应的直线在半平面交上、且存在一个在半平面交上的整点满足至多存在\(1\)个排名为\(1\)的人覆盖这个整点。

对于\(M\)更大的情况按照上面的方法做下去即可。复杂度\(O(MNlogN)\)

一些细节:为了避免精度误差、更好地判断“一条线段覆盖的横坐标范围内没有整点”的条件,可以使用分数类计算两条直线的交点;半平面交上的二分步骤大概是先二分出斜率小于当前直线和大于当前直线的区域,然后在这两个区域内分别二分,如果某些区域不存在则需要加一些特别的判断;区间覆盖可以差分进行,也就是记录下每一个会让答案修改的位置……

代码

posted @ 2019-04-16 21:28  cjoier_Itst  阅读(593)  评论(0编辑  收藏  举报