[Luogu P3702] [BZOJ 4818] [SDOI2017]序列计数

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/85059226

洛谷传送门

BZOJ传送门

题目描述

Alice想要得到一个长度为 n n 的序列,序列中的数都是不超过 m m 的正整数,而且这 n n 个数的和是 p p 的倍数。

Alice还希望,这 n n 个数中,至少有一个数是质数。

Alice想知道,有多少个序列满足她的要求。

输入输出格式

输入格式:

一行三个数, n , m , p n,m,p

输出格式:

一行一个数,满足Alice的要求的序列数量,答案对 20170408 20170408 取模。

输入输出样例

输入样例#1:

3 5 3

输出样例#1:

33

说明

20 % 20\% 的数据, 1 n , m 100 1\leq n,m\leq100

50 % 50\% 的数据, 1 m 100 1\leq m \leq 100

80 % 80\% 的数据, 1 m 1 0 6 1\leq m\leq 10^6

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

解题分析

保证有质数这个条件很难弄, 我们就用所有的方案减去没有质数的方案就可以了。

d p [ i ] [ j ] dp[i][j] 表示前 i i 个数选取总和模 p p 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] , 其中 c n t [ k ] cnt[k] 为范围内模 p p k k 的数的数量。

然后我们发现, 似乎这玩意的转移很套路? 那我们可以写成矩阵的形式, 下图是 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]
就可以中间矩阵快速幂搞搞, 然后就 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);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/85059226