题目传送门: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]) 表示最左右两侧的隔板都被跨过了的贡献总和。
那么它们就可以互相转移了:
形式差不多,具体就是第一项是其中没有其它隔板的情况,第二项是倒数第二个隔板没被跨过的情况,第三项是倒数第二个隔板被跨过的情况。其中 (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) 项线性递推式:
可以得到如下代码:
#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