BZOJ 4818

感觉不难。

首先转化一下题目,“至少有一个质数”$=$“全部方案”$ - $“一个质数也没有”。

注意到$m \leq 2e7$,$[1, m]$内的质数可以直接筛出来。

设$f_{i, j}$表示当前长度序列为$i$,当前和模$p$的值是$j$的方案数,直接无脑枚举$m$转移复杂度是$O(nmp)$的,但是发现每一次转移形式都是相同的。

$$f_{i, x} = \sum f_{i - 1, y}(y + z \equiv x(\mod p))$$

其实在模$p$的意义下大于等于$p$的数可以直接归类到这个数模$p$这一档里面,也就是说,我们可以记一个$cnt_x$表示模$p$意义下相同的数有$x$个。

$$f_{i, (x + y) \mod p} = \sum f_{i - 1, x} \times cnt_y$$

发现这个式子的形式很像矩阵快速幂的样子,然后就把转移写成矩阵的形式快速幂一下就好了。

转移矩阵的第$(i, j)$个格子是$\sum_{(i + k) \equiv j(\mod p)}cnt_k$

时间复杂度$O(m + p^3logn)$。

咕,感觉时间刚刚好。

然而再次观察一下这个式子发现是一个卷积的形式,因此可以直接$NTT$,时间复杂度可以降到$O(m + plogplogn)$,但是在这题中$p$太小了$ + $模数不好,直接暴力卷积的时间表现应该比$NTT$要优秀。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll; const int N = 2e7 + ;
const int M = ;
const ll P = 20170408LL; int n, m, K, pCnt = , pri[N], cnt[M];
bool np[N]; template <typename T>
inline void inc(T &x, T y) {
x += y;
if (x >= P) x -= P;
} template <typename T>
inline void sub(T &x, T y) {
x -= y;
if (x < ) x += P;
} struct Matrix {
int tn, tm;
ll s[M][M]; inline void init() {
tn = tm = ;
memset(s, , sizeof(s));
} friend Matrix operator * (const Matrix x, const Matrix y) {
Matrix res;
res.init();
res.tn = x.tn, res.tm = y.tm;
for (int k = ; k < x.tm; k++)
for (int i = ; i < x.tn; i++)
for (int j = ; j < y.tm; j++)
inc(res.s[i][j], x.s[i][k] * y.s[k][j] % P);
return res;
} inline Matrix fpow(int y) {
Matrix x = *this, res;
res.init();
res.tn = x.tn, res.tm = x.tm;
for (int i = ; i < x.tn; i++) res.s[i][i] = ;
for (; y ; y >>= ) {
if (y & ) res = res * x;
x = x * x;
}
return res;
} inline void print() {
for (int i = ; i < tn; i++)
for (int j = ; j < tm; j++)
printf("%lld%c", s[i][j], " \n"[j == tm - ]);
printf("\n");
} } trans, ans; inline void sieve() {
np[] = ;
for (int i = ; i <= m; i++) {
if (!np[i]) pri[++pCnt] = i;
for (int j = ; j <= pCnt && pri[j] * i <= m; j++) {
np[i * pri[j]] = ;
if (i % pri[j] == ) break;
}
}
} inline ll solve1() {
memset(cnt, , sizeof(cnt));
for (int i = ; i <= m; i++) ++cnt[i % K]; trans.init();
trans.tn = trans.tm = K;
for (int i = ; i < K; i++)
for (int j = ; j < K; j++)
inc(trans.s[i][(i + j) % K], 1LL * cnt[j]);
// trans.print(); trans = trans.fpow(n); // trans.print(); ans.init();
ans.s[][] = ;
ans.tn = , ans.tm = K;
ans = ans * trans;
return ans.s[][];
} inline ll solve2() {
sieve();
memset(cnt, , sizeof(cnt));
for (int i = ; i <= m; i++)
if (np[i]) ++cnt[i % K]; /* for (int i = 0; i < K; i++)
printf("%d%c", cnt[i], " \n"[i == K - 1]); */ trans.init();
trans.tn = trans.tm = K;
for (int i = ; i < K; i++)
for (int j = ; j < K; j++)
inc(trans.s[i][(i + j) % K], 1LL * cnt[j]);
// trans.print(); trans = trans.fpow(n); // trans.print(); ans.init();
ans.s[][] = ;
ans.tn = , ans.tm = K;
ans = ans * trans;
return ans.s[][];
} int main() {
scanf("%d%d%d", &n, &m, &K);
// printf("%lld\n", solve1());
// printf("%lld\n", solve2());
printf("%lld\n", (solve1() - solve2() + P) % P);
return ;
}

最新文章

  1. MFC -- 遍历Dialog中的子元素(控件)
  2. array_multisort 的详细使用方法
  3. phpstorm
  4. ggplot2颜色操作
  5. hdu4825 字典树 XOR
  6. Js 访问Aspnet后台页面变量
  7. iOS学习之C语言指针
  8. 鸟哥私房菜笔记:Iptables:数据包过滤软件
  9. 【转】如何设置无线路由器的信道以获得最佳WIFI体验?
  10. cf475A Bayan Bus
  11. Hibernate缓存和状态
  12. 编译phoneix源码,整合Hbase
  13. ubantu 安装 wget
  14. axure--中继器
  15. mysql数据库通过二进制 -【恢复数据记录】
  16. Python 中的垃圾回收机制--备忘
  17. lepus部署
  18. hdu 5079 Square
  19. 【DevOps】谁说大象不能跳舞?
  20. Python3基础 super 子类调用父类的__init__

热门文章

  1. Oracle联合查询
  2. java实现MsOffice文档向pdf文档转化
  3. 【Python】matplotlib 双y轴绘制及合并图例
  4. C#调用OCR组件识别图片文字
  5. phpmyadmin不允许一个表创建多个主键的解决办法
  6. linux shell 模拟post请求
  7. Ubuntu 16.04 natural scrolling
  8. ExtJs 扩展类CheckColumn的使用(事件触发)
  9. UTF-8中的BOM
  10. [题解] [NOIP2008] 双栈排序——关系的冲突至图论解法