zoukankan      html  css  js  c++  java
  • Codeforces 848E: Days of Floral Colours

    题目传送门:CF848E

    题解

    我们称一对相距为 (n) 的同色花为一个「隔板」。

    将题目中定义的连续段,向逆时针方向扩展一格,也就是也把作为隔板的花包含进去。

    由于花钟是对称的,显然美丽度是完全平方数,因为一个连续段中心对称后还是一样的连续段。

    长度为 (m) 的连续段会为答案产生 ({(m - 1)}^2) 的乘法贡献(只计算一边)。

    环上相对不好考虑,我们还是考虑破环为链,因为是对称的,我们只需考虑 (1 sim n) 中的所有花。

    可是 (1) 所在的连续段可能和 (n) 所在的连续段是跨过分界点相连的,这该怎么办?

    我们这样考虑:对于 (1) 所在连续段的最「左」(想象这是上半圆弧)侧点(也就是那个隔板),它可能在 (1) 本身的位置,也有可能在 (n + 2 sim 2 n) 之间,把它的位置强行顺时针移动到 (1) 的位置,这样就不会出现跨过分界点相连的情况。但是会统计漏情况,就是那些隔板不在 (1) 本身的位置上的情况,解决的方法是枚举这一段的长度 (m),那么就有 (m) 种旋转方案了,具体细节下面展开讨论。

    总之经过转换我们可以看成是只需统计序列上而非环上的情况。

    我们是以每个隔板,也就是每个连续段一起转移的,先考虑这样的简单情况:

    长度为 (m) 的一段,只允许段内连边,且段内不能有隔板,问方案数,令其为 (g[m])

    段内是没有隔板的,那就只有两种最小单元的连边形式:((i) leftrightarrow (i + 1))((i) leftrightarrow (i + 2), (i + 1) leftrightarrow (i + 3))

    一个占用连续两个位置,另一个占用连续四个位置。显然:(g[0] = 1)(g[m] = g[m - 2] + g[m - 4])

    如果斐波那契数列是 (f),其实有 (f[n + 1] = g[2 n]),不过这不是关键点,只是为了帮助理解。

    这个数列在一定意义下确实可以表示一个连续段了,只不过还有点瑕疵,因为连续段可不止一种。

    其实有 (4) 种(或 (3) 种)连续段,根据该连续段左右侧的隔板的情况分类:

    • 左右侧隔板都没有被一对距离为 (2) 的同色花对跨过。
    • 左侧隔板被跨过,右侧隔板没被跨过。
    • 左侧隔板没被跨过,右侧隔板被跨过。
    • 左右侧隔板都被跨过。

    中间两类是对称的所以计数的时候求出来的值肯定都相同。

    (1) 种连续段,长度为 (m) 时方案数就是 (g[m - 1])

    (2, 3) 种连续段,长度为 (m) 时方案数就是 (g[m - 2])

    (4) 种连续段,长度为 (m) 时方案数就是 (g[m - 3])

    然而每一种对答案都产生 ({(m - 1)}^2) 的乘法贡献。

    然后就可以做 DP 了,每次转移一整个连续段。

    每个连续段需要根据左右侧隔板的状态来确定方案,一个连续段的右侧隔板同时是下一个连续段的左侧隔板。

    也就是说 DP 状态中记录下上个连续段的右侧隔板的状态是有必要的。

    实际上记录左侧隔板的状态也是有必要的,这与我们前面提到的最后把序列上的答案转换回环上有关。

    总之我们定义下列几个序列:

    • (f_0[m]) 表示连续段拼起来总长为 (m),且最左侧、最右侧隔板都没被跨过,每种方案的贡献总和。
    • (f_1[m]) 表示最左侧或最右侧恰有一侧的隔板被跨过的贡献总和,由对称性它们俩的值相同。
    • (f_2[m]) 表示最左右两侧的隔板都被跨过了的贡献总和。

    那么它们就可以互相转移了:

    [egin{aligned} f_0[m] &= {(m - 1)}^2 g[m - 1] + sum_{i = 1}^{m - 1} f_0[m - i] {(i - 1)}^2 g[i - 1] + sum_{i = 2}^{m - 2} f_1[m - i] {(i - 1)^2} g[i - 2] \ f_1[m] &= {(m - 1)}^2 g[m - 2] + sum_{i = 2}^{m - 1} f_0[m - i] {(i - 1)}^2 g[i - 2] + sum_{i = 3}^{m - 2} f_1[m - i] {(i - 1)^2} g[i - 3] \ f_2[m] &= {(m - 1)}^2 g[m - 3] + sum_{i = 2}^{m - 2} f_1[m - i] {(i - 1)}^2 g[i - 2] + sum_{i = 3}^{m - 3} f_2[m - i] {(i - 1)^2} g[i - 3] end{aligned} ]

    形式差不多,具体就是第一项是其中没有其它隔板的情况,第二项是倒数第二个隔板没被跨过的情况,第三项是倒数第二个隔板被跨过的情况。其中 (f_1[m]) 的递推式假设最左侧隔板没被跨过,而最右侧隔板被跨过了。

    这样直接转移,复杂度是 (mathcal O (n^2)) 的,如果你使用了 CDQ 多项式乘法就可以成功优化到 (mathcal O (n log^2 n))

    算出 (f_{0, 1, 2}) 后,最后一步就是把序列的答案还原成环了。枚举位置 (1) 所在的连续段的长度以及属于哪一类,剩下的就可以看作是序列了,然后套用 (f_{0, 1, 2}) 得到剩下部分的答案即可。

    到这里应该是做完了,这里是代码。但是据说有人十分丧心病狂,令

    • (displaystyle G_0 = mathbf{OGF} left( { left{ {(i - 1)}^2 g[i - 1] ight} }_{i = 1}^{infty} ight))
    • (displaystyle G_1 = mathbf{OGF} left( { left{ {(i - 1)}^2 g[i - 2] ight} }_{i = 2}^{infty} ight))
    • (displaystyle G_2 = mathbf{OGF} left( { left{ {(i - 1)}^2 g[i - 3] ight} }_{i = 3}^{infty} ight))

    以及 (F_0 = mathbf{OGF}(f_0), F_1 = mathbf{OGF}(f_1), F_2 = mathbf{OGF}(f_2))

    然后把它们都算出来(用封闭形式表示),结果发现它们都是多项式除以多项式的形式,十分优美。

    而且分母是低阶多项式,就可以线性求逆了,然后在 (mathcal O (n)) 的时间内求出 (f_{0, 1, 2}),再解决序列变成环的问题。

    其实可以更进一步推出最终答案的生成函数然后线性递推。不过暴力写完,整一个 BM 它不香吗?

    搞了前 (120) 项扔进 BM 可以搞出一个 (16) 项线性递推式:

    [{0, 4, 8, -1, 16, -10, 4, -12, -48, 26, -44, 15, -16, -4, -4, -1} ]

    可以得到如下代码:

    #include <cstdio>
    #include <algorithm>
    
    typedef long long LL;
    const int Mod = 998244353;
    
    int N, Ans;
    
    int main() {
    	scanf("%d", &N);
    	static const int R[16] = {0, 4, 8, -1, 16, -10, 4, -12, -48, 26, -44, 15, -16, -4, -4, -1};
    	int A[16] = {0, 0, 0, 24, 4, 240, 204, 1316, 2988, 6720, 26200, 50248, 174280, 436904, 1140888, 3436404};
    	for (int i = 16; i <= N; ++i) {
    		int x = 0;
    		for (int j = 0; j < 16; ++j)
    			x = (x + (LL)R[(i - j - 1) & 15] * A[j]) % Mod;
    		A[i & 15] = x;
    	}
    	printf("%d
    ", (A[N & 15] + Mod) % Mod);
    	return 0;
    } // check 848E.cpp for CDQFFT
    
  • 相关阅读:
    LC.225. Implement Stack using Queues(using two queues)
    LC.232. Implement Queue using Stacks(use two stacks)
    sort numbers with two stacks(many duplicates)
    LC.154. Find Minimum in Rotated Sorted Array II
    LC.81. Search in Rotated Sorted Array II
    LC.35.Search Insert Position
    前后端分离:(一)
    Redis基本使用(一)
    GIT篇章(二)
    GIT篇章(一)
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/CF848E.html
Copyright © 2011-2022 走看看