洛谷传送门
BZOJ传送门
题目描述
Alice想要得到一个长度为 n n n的序列,序列中的数都是不超过 m m m的正整数,而且这 n n n个数的和是 p p p的倍数。
Alice还希望,这 n n n个数中,至少有一个数是质数。
Alice想知道,有多少个序列满足她的要求。
输入输出格式
输入格式:
一行三个数, n , m , p n,m,p n,m,p。
输出格式:
一行一个数,满足Alice的要求的序列数量,答案对 20170408 20170408 20170408取模。
输入输出样例
输入样例#1:
3 5 3
输出样例#1:
33
说明
对 20 % 20\% 20%的数据, 1 ≤ n , m ≤ 100 1\leq n,m\leq100 1≤n,m≤100
对 50 % 50\% 50%的数据, 1 ≤ m ≤ 100 1\leq m \leq 100 1≤m≤100
对 80 % 80\% 80%的数据, 1 ≤ m ≤ 1 0 6 1\leq m\leq 10^6 1≤m≤106
对 100 % 100\% 100%的数据, 1 ≤ n ≤ 1 0 9 , 1 ≤ m ≤ 2 × 1 0 7 , 1 ≤ p ≤ 100 1\leq n \leq 10^9,1\leq m \leq 2\times 10^7,1\leq p\leq 100 1≤n≤109,1≤m≤2×107,1≤p≤100
解题分析
保证有质数这个条件很难弄, 我们就用所有的方案减去没有质数的方案就可以了。
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个数选取总和模 p p p余 j j j的方案数之和, 显然有 d p [ i ] [ j ] = ∑ k = 0 p − 1 d p [ i − 1 ] [ ( j − k + p ) m o d p ] ∗ c n t [ k ] dp[i][j]=\sum_{k=0}^{p-1}dp[i-1][(j-k+p)\ mod\ p]*cnt[k] dp[i][j]=∑k=0p−1dp[i−1][(j−k+p) mod p]∗cnt[k], 其中 c n t [ k ] cnt[k] cnt[k]为范围内模 p p p余 k k k的数的数量。
然后我们发现, 似乎这玩意的转移很套路? 那我们可以写成矩阵的形式, 下图是 p = 3 p=3 p=3的一个样例:
[
d
p
[
0
]
[
0
]
d
p
[
0
]
[
1
]
d
p
[
0
]
[
2
]
]
×
[
c
n
t
[
0
]
c
n
t
[
1
]
c
n
t
[
2
]
c
n
t
[
2
]
c
n
t
[
0
]
c
n
t
[
1
]
c
n
t
[
1
]
c
n
t
[
2
]
c
n
t
[
0
]
]
=
[
d
p
[
1
]
[
0
]
d
p
[
1
]
[
1
]
d
p
[
1
]
[
2
]
]
\left[ \begin{matrix} dp[0][0] & dp[0][1] & dp[0][2] \end{matrix} \right] \times \left[ \begin{matrix} cnt[0] & cnt[1] & cnt[2] \\ cnt[2] & cnt[0] & cnt[1] \\ cnt[1] & cnt[2] & cnt[0] \end{matrix} \right] = \left[ \begin{matrix} dp[1][0] & dp[1][1] & dp[1][2] \end{matrix} \right]
[dp[0][0]dp[0][1]dp[0][2]]×⎣⎡cnt[0]cnt[2]cnt[1]cnt[1]cnt[0]cnt[2]cnt[2]cnt[1]cnt[0]⎦⎤=[dp[1][0]dp[1][1]dp[1][2]]
就可以中间矩阵快速幂搞搞, 然后就
A
A
A了。
代码如下:
#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);
}