题面

传送门

分析

考虑容斥原理,用总的方案数-不含质数的方案数

设\(dp1[i][j]\)表示前i个数,和取模p为j的方案数,

\(dp2[i][j]\)表示前i个数,和取模p为j的方案数,且所有的数均不为质数

[1,m]中的质数可以线性筛出

则\(dp1[i][j]=dp1[i-1][((j-k) \mod p+p)\mod p],j \in [0,p-1],k \in [0,m]\)

\(dp2[i][j]=dp1[i-1][((j-k) \mod p+p)\mod p],j \in [0,p-1],k \in [0,m]且不为质数\)

最终答案为\(dp1[n][0]-dp2[n][0]\)

其中k表示第i位选的数,((j-k)%p+p)%p为前i位的和,这里的减法是带模减法,是为了防止负数取模造成的问题

该算法的时间复杂度为\(O(nmp)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 105
#define mod 20170408
using namespace std;
int n,m,p; long long dp1[maxn][maxn],dp2[maxn][maxn];
int cnt=0;
int vis[maxn];
int prime[maxn];
void sieve(int n){
vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&(long long)i*prime[j]<=(long long)n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
} int main(){
scanf("%d %d %d",&n,&m,&p);
sieve(m);
dp1[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<p;j++){
for(int k=1;k<=m;k++){
dp1[i][j]+=dp1[i-1][((j-k)%p+p)%p];
dp1[i][j]%=mod;
}
}
}
dp2[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<p;j++){
for(int k=1;k<=m;k++){
if(vis[k]==0) continue;
dp2[i][j]+=dp2[i-1][((j-k)%p+p)%p];
dp2[i][j]%=mod;
}
}
}
printf("%lld\n",dp1[n][0]-dp2[n][0]);
}

有一个小优化,在转移的过程中我们不关心k的值,而是关心k%p的值,所以我们把[1,m]中的数按模p的余数分类

设cntm[i]表示[1,m]中的数%p余i的个数

cnth[i]表示[1,m]中的合数%p余i的个数

则上述状态转移方程可以改写为

\(dp1[i][j]=dp1[i-1][((j-k) \mod p+p)\mod p] \times cntm[k],j \in [0,p-1],k \in [0,p-1]\)

\(dp2[i][j]=dp1[i-1][((j-k) \mod p+p)\mod p] \times cnth[k] ,j \in [0,p-1],k \in [0,p-1]且不为质数\)

我们发现从i-1到i的转移是确定的,可以用矩阵快速幂优化

我们来构造转移矩阵

\(\begin{bmatrix} dp1[i][0] \\ dp1[i][1]\\ \vdots \\dp1[i][p-1]\end{bmatrix} = \begin{bmatrix} cntm[0] \ cntm[p-1] \ cntm[p-2] \ \dots \ cntm[1] \\cntm[1] \ cntm[0] \ cntm[p-1] \ \dots \ cntm[2] \\ \vdots \\ cntm[p-1] \ cntm[p-2] \ cntm[p-3] \ \dots \ cntm[0] \end{bmatrix} \times \begin{bmatrix} dp1[i-1][0] \\ dp1[i-1][1]\\ \vdots \\dp1[i-1][p-1] \end{bmatrix}\)

转移矩阵的第i行第j列为cntm[(i-j+p)%p]

同理有

\(\begin{bmatrix} dp2[i][0] \\ dp2[i][1]\\ \vdots \\dp2[i][p-1]\end{bmatrix} = \begin{bmatrix} cnth[0] \ cnth[p-1] \ cnth[p-2] \ \dots \ cnth[1] \\cnth[1] \ cnth[0] \ cnth[p-1] \ \dots \ cnth[2] \\ \vdots \\ cnth[p-1] \ cnth[p-2] \ cnth[p-3] \ \dots \ cnth[0] \end{bmatrix} \times \begin{bmatrix} dp2[i-1][0] \\ dp2[i-1][1]\\ \vdots \\dp2[i-1][p-1] \end{bmatrix}\)

转移矩阵的第i行第j列为cnth[(i-j+p)%p]

注意\(dp1[0][i]\)的初始值为cntm[i]

所以

\(\begin{bmatrix} dp1[n][0] \\ dp1[n][1]\\ \vdots \\dp1[n][p-1]\end{bmatrix} = \begin{bmatrix} cntm[0] \ cntm[p-1] \ cntm[p-2] \ \dots \ cntm[1] \\cntm[1] \ cntm[0] \ cntm[p-1] \ \dots \ cntm[2] \\ \vdots \\ cntm[p-1] \ cntm[p-2] \ cntm[p-3] \ \dots \ cntm[0] \end{bmatrix}^{n-1} \times \begin{bmatrix} cntm[0] \\ cntm[1]\\ \vdots \\cntm[p-1] \end{bmatrix}\)

\(\begin{bmatrix} dp2[n][0] \\ dp2[n][1]\\ \vdots \\dp2[n][p-1]\end{bmatrix} = \begin{bmatrix} cnth[0] \ cnth[p-1] \ cnth[p-2] \ \dots \ cnth[1] \\cnth[1] \ cnth[0] \ cnth[p-1] \ \dots \ cnth[2] \\ \vdots \\ cnth[p-1] \ cnth[p-2] \ cnth[p-3] \ \dots \ cnth[0] \end{bmatrix}^{n-1} \times \begin{bmatrix} cnth[0] \\ cnth[1]\\ \vdots \\cnth[p-1] \end{bmatrix}\)

时间复杂度为\(O(m+p^3 \log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 105
#define maxm 20000005
#define mod 20170408
using namespace std;
int n,m,p; int cnt=0;
int vis[maxm];
int prime[maxm];
void sieve(int n){
vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&(long long)i*prime[j]<=(long long)n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
} struct matrix{
long long a[maxn][maxn];
matrix(){
memset(a,0,sizeof(a));
}
friend matrix operator * (matrix a,matrix b){
matrix c;
for(int i=0;i<p;i++){
for(int j=0;j<p;j++){
c.a[i][j]=0;
for(int k=0;k<p;k++){
c.a[i][j]+=a.a[i][k]*b.a[k][j]%mod;
c.a[i][j]%=mod;
}
}
}
return c;
}
}; matrix fast_pow(matrix x,int k){
matrix ans;
for(int i=0;i<p;i++){
ans.a[i][i]=1;
}
while(k>0){
if(k&1) ans=ans*x;
x=x*x;
k>>=1;
}
return ans;
} int cntm[maxn],cnth[maxn];
matrix A,B;
int main(){
scanf("%d %d %d",&n,&m,&p);
sieve(m);
for(int i=1;i<=m;i++){
cntm[i%p]++;
}
for(int i=1;i<=m;i++){
if(vis[i]) cnth[i%p]++;
}
for(int i=0;i<p;i++){
for(int j=0;j<p;j++){
A.a[i][j]=cntm[(i-j+p)%p];
B.a[i][j]=cnth[(i-j+p)%p];
}
}
long long ans1=0,ans2=0;
A=fast_pow(A,n-1);
B=fast_pow(B,n-1);
for(int i=0;i<p;i++){
ans1+=cntm[i]*A.a[0][i];
ans1%=mod;
ans2+=cnth[i]*B.a[0][i];
ans2%=mod;
}
printf("%lld\n",(ans1-ans2+mod)%mod);
}

LOJ 2183 / SDOI2015 序列统计 (DP+矩阵快速幂)的更多相关文章

  1. 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

    [BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...

  2. bzoj 3992 [SDOI2015] 序列统计 —— NTT (循环卷积+快速幂)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 (学习NTT:https://riteme.github.io/blog/2016-8 ...

  3. BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*

    BZOJ5298 CQOI2018 交错序列 [DP+矩阵快速幂优化] Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻 ...

  4. bnuoj 34985 Elegant String DP+矩阵快速幂

    题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=34985 We define a kind of strings as elegant s ...

  5. HDU 5434 Peace small elephant 状压dp+矩阵快速幂

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5434 Peace small elephant  Accepts: 38  Submissions: ...

  6. 【BZOJ】2004: [Hnoi2010]Bus 公交线路 状压DP+矩阵快速幂

    [题意]n个点等距排列在长度为n-1的直线上,初始点1~k都有一辆公车,每辆公车都需要一些停靠点,每个点至多只能被一辆公车停靠,且每辆公车相邻两个停靠点的距离至多为p,所有公车最后会停在n-k+1~n ...

  7. 【BZOJ】4861: [Beijing2017]魔法咒语 AC自动机+DP+矩阵快速幂

    [题意]给定n个原串和m个禁忌串,要求用原串集合能拼出的不含禁忌串且长度为L的串的数量.(60%)n,m<=50,L<=100.(40%)原串长度为1或2,L<=10^18. [算法 ...

  8. Codeforces 621E Wet Shark and Block【dp + 矩阵快速幂】

    题意: 有b个blocks,每个blocks都有n个相同的0~9的数字,如果从第一个block选1,从第二个block选2,那么就构成12,问对于给定的n,b有多少种构成方案使最后模x的余数为k. 分 ...

  9. codeforces E. Okabe and El Psy Kongroo(dp+矩阵快速幂)

    题目链接:http://codeforces.com/contest/821/problem/E 题意:我们现在位于(0,0)处,目标是走到(K,0)处.每一次我们都可以从(x,y)走到(x+1,y- ...

随机推荐

  1. CSS高度坍塌原因及解决办法

    在文档流中,父元素的高度默认是被子元素撑开的,也就是子元素多高,父元素就多高. 但是当为子元素设置浮动以后,子元素会完全脱离文档流,此时将会导致子元素无法撑起父元素的高度,导致父元素的高度塌陷.由于父 ...

  2. 2019-3-6-WPF-使用-SharpDX

    title author date CreateTime categories WPF 使用 SharpDX lindexi 2019-03-06 16:52:37 +0800 2018-4-20 9 ...

  3. openstack stein部署手册 3. keystone

    # 建立数据库用户及权限 create database keystone; grant all privileges on keystone.* to keystone@'localhost' id ...

  4. JSON.stringify常见用法

    转摘于其他博客 var data =[ { name: "金",sex:"1",age:26 }, { name: "才",sex:&quo ...

  5. html+css+javascript学习记录1

    <p> 最近在学一部分前端,知识点很多,却没怎么系统地应用过,因而理解可能不够深吧.所以我想做点片段似的东西,不懂的再在网上搜一搜,这样可能会更有意思点,所以做了这个记录,希望自己坚持下去 ...

  6. 容器(collection)初步

    容器(集合)的分类: 泛型(generic):本质是数据类型的参数化(提前告诉编译器,在调用泛型时必须传入实际类型) 例:E即为在主函数中定义的传入的实际类型 class MyCollection&l ...

  7. 2018 ACM-ICPC 中国大学生程序设计竞赛线上赛 D Merchandise (斜率优化)

    Description: The elderly aunts always like to look for bargains and preferential merchandise. Now th ...

  8. Anaconda概念和使用方法

    Anaconda概述 Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管理与环境管理的功能,可以很方便地解决多版本python并存.切 ...

  9. IntelliJ IDEA 装配FindBugs以及应用

    IntelliJ IDEA 安装FindBugs以及应用 众所周知,项目越来越大,开发人员越来越多,我们的代码审查工作会变得越来越复杂,对代码质量控制难度也与日俱增,尽管经验丰富的程序员能审查能检查出 ...

  10. servlet技术之下载文件演示(DownloadServlet.class)

    servlet技术之下载文件演示(DownloadServlet.class) 文件是指把服务器端文件发送到客户端,Servlet能够向客户端发送任意格式的文件数据,例程的DownloadServle ...