P5320 题解

· · 题解

前言

退役选手不好好学 whk 跑来颓废

题解里都是斯特林数推式子 + 扩域,对我这样脑容量不足的暴力选手不够友好。

这里讲一个十分暴力的 BM + 线性递推做法。

前置知识不会可以左转模板题:

P4723 【模板】常系数齐次线性递推

P5487 【模板】Berlekamp–Massey 算法

解法

初步推导得到的式子都是 \sum_{i = 1}^{n} \binom{f_i}{k} 这样的形式,其中 \{f\} 为一个低阶的常系数齐次线性递推数列(以下“常系数齐次线性递推”简称为“线性递推”)。

考虑证明这是一个线性递推数列,然后就可以大力套 BM 了。

首先对与线性递推有一个非常重要的性质:线性递推的封闭性。

对于线性递推数列 \{a\},\{b\},有如下几条:

考虑原式,前缀和相当于卷上 \{1,1,1,\cdots\},可以摘掉。

剩下部分为:

\binom{f_i}{k} &= \frac{\prod_{j = 0}^{k - 1}f_i - j}{k!}\\ \end{aligned} 综上,$\sum_{i = 1}^{n} \binom{f_i}{k}$ 为一个线性递推数列,我们可以 $\mathcal{O} (k ^2)$ 暴力求出前面一定的项数然后丢进 BM 里。 BM 打表可知,最长的递推式长度不超过 $2507$,暴力求其二倍即数列前面约 $5000$ 项即可,时间复杂度 $\mathcal{O(k^2 + k \log k \log n)}$,用 $k^2 \log$ 线性递推在洛谷和 loj 都会被卡。 ## 代码 [完整代码](https://www.luogu.com.cn/paste/czm6527n) 博客里就放个核心部分吧,线性递推和 BM 都是板子。 ```cpp #define rep(a,b,c) for(int a = (b);a <= (c);++a) #define repl(a,b,c) for(int a = (b);a < (c);++a) #define add(x,y) ( (((x) += (y)) >= MOD) && ((x) -= MOD) ) #define red(x,y) ( (((x) -= (y)) < 0) && ((x) += MOD) ) // 暴力组合数 inline int binom(int n,int m) { int x = 1,y = 1; rep(i,1,m) { x = (i64)x * (n - i + 1) % MOD; y = (i64)y * i % MOD; } return (i64)x * qpow(y) % MOD; } int m; void solve() { i64 l = readI(),r = readI(); int k = readI(); int frac = qpow((r - l + 1) % MOD); if(m == 3) { r >>= 1,l = ((l - 1) >> 1) + 1; if(l > r) { PutC('0'),enter; return ; } } const int lim = 5000;// 暴力计算前 5000 项 g[0] = 1; rep(i,1,lim) { g[i] = g[i - 1],f[i] = f[i - 1]; if(m == 3) { g[i] = g[i] * 4ll % MOD; if(i ^ 1) add(g[i],MOD - g[i - 2]); else add(g[i],MOD - 1); } else if(i ^ 1) add(g[i],g[i - 2]); add(f[i],binom(g[i],k)); } int kk = BerlekampMassey(f,a,lim);// BM 求递推式 int ans = LinearRecurrence(f + 1,a,r - 1,kk); if(l ^ 1) add(ans,MOD - LinearRecurrence(f + 1,a,l - 2,kk)); ans = (i64)ans * frac % MOD; writeI(ans),enter; } ```