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

本文深入解析洛谷和BZOJ上一道关于寻找满足特定条件的序列数量的问题,通过矩阵快速幂的方法解决序列求和需为某数倍数且包含至少一个质数的挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
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 1n,m100

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

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

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 1n109,1m2×107,1p100

解题分析

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

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=0p1dp[i1][(jk+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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值