LOJ 2183 / SDOI2015 序列统计 (DP+矩阵快速幂)
题面
分析
考虑容斥原理,用总的方案数-不含质数的方案数
设\(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+矩阵快速幂)的更多相关文章
- 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂
[BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...
- bzoj 3992 [SDOI2015] 序列统计 —— NTT (循环卷积+快速幂)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 (学习NTT:https://riteme.github.io/blog/2016-8 ...
- BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*
BZOJ5298 CQOI2018 交错序列 [DP+矩阵快速幂优化] Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻 ...
- 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 ...
- HDU 5434 Peace small elephant 状压dp+矩阵快速幂
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5434 Peace small elephant Accepts: 38 Submissions: ...
- 【BZOJ】2004: [Hnoi2010]Bus 公交线路 状压DP+矩阵快速幂
[题意]n个点等距排列在长度为n-1的直线上,初始点1~k都有一辆公车,每辆公车都需要一些停靠点,每个点至多只能被一辆公车停靠,且每辆公车相邻两个停靠点的距离至多为p,所有公车最后会停在n-k+1~n ...
- 【BZOJ】4861: [Beijing2017]魔法咒语 AC自动机+DP+矩阵快速幂
[题意]给定n个原串和m个禁忌串,要求用原串集合能拼出的不含禁忌串且长度为L的串的数量.(60%)n,m<=50,L<=100.(40%)原串长度为1或2,L<=10^18. [算法 ...
- Codeforces 621E Wet Shark and Block【dp + 矩阵快速幂】
题意: 有b个blocks,每个blocks都有n个相同的0~9的数字,如果从第一个block选1,从第二个block选2,那么就构成12,问对于给定的n,b有多少种构成方案使最后模x的余数为k. 分 ...
- codeforces E. Okabe and El Psy Kongroo(dp+矩阵快速幂)
题目链接:http://codeforces.com/contest/821/problem/E 题意:我们现在位于(0,0)处,目标是走到(K,0)处.每一次我们都可以从(x,y)走到(x+1,y- ...
随机推荐
- js中reduce()的牛掰所在 本质作用:实现数值累计,筛选过滤,类似递归
先看w3c官说 array.reduce(function(total, currentValue, currentIndex, arr), initialValue); /* total: ...
- 表格类型数据,Excel csv导入,导出操作
import pandas # 创建表格格式# ad = pandas.DataFrame({"a": range(1, 10), "b": range(10, ...
- RSA加密原理与秘钥、公钥生成
RSA加密(非对称加密) RSA公开密钥密码体制.所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制.(不可逆向运算的加密方法) ...
- ll字段 详解 文件权限
文件类型和权限 硬链接总数 属主用户名 属组组名 文件大小(字节) 上次修改时间 文件/目录名 drwxr-xr-x 15 lb lb 4096 10月 19 01:11 ./ drwxr-xr-x ...
- js中跳出forEach循环
缘由:近期在项目中使用lodash.js中的_.foreach方法处理数据,需要在满足条件时结束循环并不执行后面的js代码. 因为foreach中默认没有break方法.在尝试中使用了return f ...
- 一、C++类库与C#类库相互调用
1.C++调用C#类库 1.准备C#类库(dll文件) 1.1.创建C#类库: 右击项目类库生成即可, 出现.dll(类库)与.pdb(pdb文件包含了编译后程序指向源代码的位置信息, 用于调试的时候 ...
- mysql 5.6 datetime default now()
CREATE TABLE `test` ( id int, `gmt_create` datetime DEFAULT NOW() not NULL )ENGINE=InnoDB; mysq ...
- 雷哥带你了解webSocket原理
偶然在知乎上看到一篇回帖,瞬间觉得之前看的那么多资料都不及这一篇回帖让我对 websocket 的认识深刻有木有.所以转到我博客里,分享一下.比较喜欢看这种博客,读起来很轻松,不枯燥,没有布道师的阵仗 ...
- python每日练习0730
""" 1. 现有面包.热狗.番茄酱.芥末酱以及洋葱,数字显 示有多少种订购组合, 其中面包必订,0 不订,1 订,比如 10000,表示只订购面包 "&quo ...
- C# 常用的ToString("xxxx")
Convert.ToDecimal("-123").ToString("#,#.##") 结果:-123 Convert.ToDecimal("-12 ...