版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/85059226
洛谷传送门
BZOJ传送门
题目描述
Alice想要得到一个长度为 的序列,序列中的数都是不超过 的正整数,而且这 个数的和是 的倍数。
Alice还希望,这 个数中,至少有一个数是质数。
Alice想知道,有多少个序列满足她的要求。
输入输出格式
输入格式:
一行三个数, 。
输出格式:
一行一个数,满足Alice的要求的序列数量,答案对 取模。
输入输出样例
输入样例#1:
3 5 3
输出样例#1:
33
说明
对 的数据,
对 的数据,
对 的数据,
对 的数据,
解题分析
保证有质数这个条件很难弄, 我们就用所有的方案减去没有质数的方案就可以了。
设 表示前 个数选取总和模 余 的方案数之和, 显然有 , 其中 为范围内模 余 的数的数量。
然后我们发现, 似乎这玩意的转移很套路? 那我们可以写成矩阵的形式, 下图是 的一个样例:
就可以中间矩阵快速幂搞搞, 然后就
了。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cstring>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MOD 20170408ll
#define MX 20005000
struct Matrix
{
ll mat[105][105];
Matrix () {std::memset(mat, 0, sizeof(mat));}
} unit, st1, st2, tran1, tran2;
int pcnt, n, m, p;
int pri[MX], cnt1[105], cnt2[105];
bool npr[MX];
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret;
R int i, j, k;
for (i = 0; i < p; ++i)
for (j = 0; j < p; ++j)
for (k = 0; k < p; ++k)
ret.mat[i][j] += x.mat[i][k] * y.mat[k][j];
for (i = 0; i < p; ++i)
for (j = 0; j < p; ++j)
ret.mat[i][j] %= MOD;
return ret;
}
IN Matrix fpow(Matrix base, R int tim)
{
Matrix ret = unit;
W (tim)
{
if (tim & 1) ret = ret * base;
base = base * base, tim >>= 1;
}
return ret;
}
void pre()
{
cnt2[1 % p]++;
R int i, j, tar;
for (i = 2; i <= m; ++i)
{
if (!npr[i]) pri[++pcnt] = i;
else cnt2[i % p]++;
for (j = 1; j <= pcnt; ++j)
{
tar = i * pri[j];
if (tar > m) break;
npr[tar] = true;
if (!(i % pri[j])) break;
}
}
int base = m / p, sur = m % p;
for (i = 0; i < p; ++i) cnt1[i] = base, unit.mat[i][i] = 1;
for (i = 1; i < p; ++i) cnt1[i] += (i <= sur);
for (j = 0; j < p; ++j)
{
for (i = 0; i < p; ++i)
{
tran1.mat[i][j] = cnt1[(j + p - i) % p];
tran2.mat[i][j] = cnt2[(j + p - i) % p];
}
}
for (i = 0; i < p; ++i) st1.mat[0][i] = cnt1[i], st2.mat[0][i] = cnt2[i];
}
int main(void)
{
scanf("%d%d%d", &n, &m, &p); pre();
printf("%lld", ((st1 * fpow(tran1, n - 1)).mat[0][0] - (st2 * fpow(tran2, n - 1)).mat[0][0] + MOD) % MOD);
}