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
    
  • 相关阅读:
    HTTP 返回状态代码详细解释
    丁一的作业
    getIntent().getExtras().clear()未清空Bundle的数据
    activity android:launchMode="singleTask" 没用重现启动activity的问题
    判断email格式
    判断网络是否可用
    修改系统语言
    生成UUID
    css reset file
    智能指针(auto_ptr)vc版
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/CF848E.html
Copyright © 2011-2022 走看看