题面:

传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4818

Solution

看到这道题,我们不妨先考虑一下20分怎么搞

想到暴力,本蒟蒻第一反应就是dfs,想法也很简单:

枚举n个数中的每一个数,枚举完每一种情况都判断一下是否满足要求

复杂度O(n^m)

显然,这样的复杂度一分都得不到,但是可以作为对拍用的暴力程序

既然dfs行不通了,那我们换个想法吧,考虑一下用dp来搞这个问题

设 f[i][j] 表示选到第i个数,前i个数的总和%p为j

转移也很好写

我们枚举一下上一个数字是啥就好

f[i][j]= sigma f[i-1][((j-k)%p+p)%p]   k:[1,m]

i:[1,n] j:[0,p-1] 

注意一下: j-k有可能是负数,所以要用负数取模的方法

初始化 f[0][0]=1 (没有数字时,仅有总和为0的情况有一种可行方法)

题目要求的有质数用一个简单的容斥就可以了

我们再做一个没有质数的dp,转移方程跟上面一样,仅需要保证 k 不为质数就行

最后将两者的i为n,j为0的状态相减就是最后答案了.

时间复杂度 O(n*p*m),20分

接下来,我们可以考虑一个很妙的优化

我们发现上面的转移方程

f[i][j]= sigma f[i-1][((j-k)%p+p)%p]   k:[1,m]

i:[1,n] j:[0,p-1]

j是从0~p-1的,而k是从1~m的

这说明了,f[i-1][j]中的某些项是会重复计算到下一个状态的

这样子,我们可以考虑做一个预处理,减少重复计算造成的时间的浪费

考虑这样做:

我们通过一个O(m)的预处理,计算出每一个从0~p-1的数可能从多少个1~m中的数%p计算而得

用一个tot[k]存储下来,tot[k]的意义为:1~m的数%p为k的有多少个

那么这样子,我们的转移方程可优化成这样子

f[i][j]= sigma f[i-1][((j-k)%p+p)%p]*tot[k]   k:[0,p-1]

i:[1,n] j:[0,p-1] 

因为 (j-k)%p = j%p - k%p;

所以说,每一个f[i-1][j%p - k%p]被算的次数仅与有多少个 K1%p=K2%p=K3%p=....有关

我们可以设K1%p=K2%p=K3%p=...=y

原式就可以变为f[i][j]=sigma f[i][j%p-y]*tot[y]

而tot[y]已在前面的预处理解决了

这样,时间复杂度就成功的降为了:O(n*p*p)

然而并没有什么卵用,因为出题者并没有设计这一档的分

我们再考虑一个优化

f[i][j]= sigma f[i-1][((j-k)%p+p)%p]*tot[k]   k:[0,p-1]

i:[1,n] j:[0,p-1] 

原转移式是不是有一个特征?对,那就是式子是固定死的,这意味着我们可以用矩阵优化至O(m^3long n)

这种类型的转移矩阵我称为"一层层"的转移,可以考虑这样列转移矩阵

然后就OK啦

Code

//Luogu P3702 [SDOI2017]序列计数
//Apr,11th,2018
//矩阵加速DP
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int P=100+10;
const int M=20000000+100;
const int poi=20170408;
struct MAT
{
int x,y;
long long a[P][P];
MAT (int tx,int ty)
{
x=tx,y=ty;
memset(a,0,sizeof a);
}
friend MAT operator * (MAT A,MAT B)
{
MAT ans=MAT(B.x,A.y);
for(int i=1;i<=ans.y;i++)
for(int j=1;j<=ans.x;j++)
for(int k=1;k<=A.x;k++)
{
ans.a[i][j]+=A.a[i][k]*B.a[k][j];
if(ans.a[i][j]>=poi) ans.a[i][j]%=poi;
}
return ans;
}
};
MAT FastPow(MAT a,int b)
{
if(b==1) return a;
MAT ans=FastPow(a,b/2);
ans=ans*ans;
if(b%2==1) ans=ans*a;
return ans;
}
int n,m,p,tot[P];
bool IsPrime[M];
int prime[M],p_tot;
void GetPrime()
{
memset(IsPrime,1,sizeof IsPrime);
IsPrime[0]=IsPrime[1]=0;
for(int i=2;i<=m;i++)
{
if(IsPrime[i]==true) prime[++p_tot]=i;
for(int j=1;j<=p_tot and prime[j]*i<=m;j++)
{
IsPrime[prime[j]*i]=false;
if(i%prime[j]==0) break;
}
}
}
int main()
{
n=read(),m=read(),p=read(); for(int i=1;i<=m;i++)
tot[i%p]++;
GetPrime();
MAT A=MAT(p,p);
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
A.a[i][j]=tot[((i-j)%p+p)%p];
MAT B=MAT(1,p);
for(int i=1;i<=p;i++)
B.a[i][1]=tot[i-1];
long long ans=(FastPow(A,n-1)*B).a[1][1]; memset(tot,0,sizeof tot);
for(int i=1;i<=m;i++)
if(IsPrime[i]==false)
tot[i%p]++;
for(int i=1;i<=p;i++)
for(int j=1;j<=p;j++)
A.a[i][j]=tot[((i-j)%p+p)%p];
for(int i=1;i<=p;i++)
B.a[i][1]=tot[i-1];
ans-=(FastPow(A,n-1)*B).a[1][1]; printf("%lld",(ans%poi+poi)%poi);
return 0;
}

C++

[BZOJ 4818/LuoguP3702][SDOI2017] 序列计数 (矩阵加速DP)的更多相关文章

  1. [Sdoi2017]序列计数 矩阵优化dp

    题目 https://www.lydsy.com/JudgeOnline/problem.php?id=4818 思路 先考虑没有质数限制 dp是在同余系下的,所以\(f[i][j]\)表示前i个点, ...

  2. [Sdoi2017]序列计数 [矩阵快速幂]

    [Sdoi2017]序列计数 题意:长为\(n \le 10^9\)由不超过\(m \le 2 \cdot 10^7\)的正整数构成的和为\(t\le 100\)的倍数且至少有一个质数的序列个数 总- ...

  3. 【BZOJ4818】【SDOI2017】序列计数 [矩阵乘法][DP]

    序列计数 Time Limit: 30 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Alice想要得到一个长度为n的序 ...

  4. Luogu3702 SDOI2017 序列计数 矩阵DP

    传送门 不考虑质数的条件,可以考虑到一个很明显的$DP:$设$f_{i,j}$表示选$i$个数,和$mod\ p=j$的方案数,显然是可以矩阵优化$DP$的. 而且转移矩阵是循环矩阵,所以可以只用第一 ...

  5. bzoj 4818: [Sdoi2017]序列计数【容斥原理+dp+矩阵乘法】

    被空间卡的好惨啊---- 参考:http://blog.csdn.net/coldef/article/details/70305596 容斥,\( ans=ans_{没有限制}-ans{没有质数} ...

  6. BZOJ 4818 [Sdoi2017]序列计数 ——矩阵乘法

    发现转移矩阵是一个循环矩阵. 然后循环矩阵乘以循环矩阵还是循环矩阵. 据说还有FFT并且更优的做法. 之后再看吧 #include <map> #include <cmath> ...

  7. 【bzoj4818】[Sdoi2017]序列计数 矩阵乘法

    原文地址:http://www.cnblogs.com/GXZlegend/p/6825132.html 题目描述 Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的 ...

  8. [BZOJ 4818] [SDOI 2017] 序列计数

    Description Alice想要得到一个长度为 \(n\) 的序列,序列中的数都是不超过 \(m\) 的正整数,而且这 \(n\) 个数的和是 \(p\) 的倍数. Alice还希望,这 \(n ...

  9. luoguP3702 [SDOI2017]序列计数

    https://www.luogu.org/problemnew/show/P3702 题目让我们在 $ [1, m] $ 从中选出 $ n $ 个数,当中要有 > $ 0 $ 个质数,和是 $ ...

随机推荐

  1. Azure Cosmos DB (一) 入门介绍

    一,引言 今天是国庆.中秋双节房价的第三天,今天抽时间分享一篇关于使用Azure 提供的一项NoSql 服务-----Azure Cosmos DB.就有人问了,我听说过 MongoDB.Redis ...

  2. 票房和口碑称霸国庆档,用 Python 爬取猫眼评论区看看电影《我和我的家乡》到底有多牛

    今年的国庆档电影市场的表现还是比较强势的,两名主力<我和我的家乡>和<姜子牙>起到了很好的带头作用. <姜子牙>首日破 2 亿,一举刷新由<哪吒之魔童降世&g ...

  3. 一种基于均值不等式的Listwise损失函数

    一种基于均值不等式的Listwise损失函数 1 前言 1.1 Learning to Rank 简介 Learning to Rank (LTR) , 也被叫做排序学习, 是搜索中的重要技术, 其目 ...

  4. P6810 「MCOI-02」Convex Hull 凸包

    Link 一句话题意: 求出 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\tau(i)\tau(j)\tau(gcd(i,j))\) 前置知识 \(diri ...

  5. 查杀进程小工具——WPF和MVVM初体验

    最近因为工作需要,研究了一下桌面应用程序.在winform.WPF.Electron等几种技术里,最终选择了WPF作为最后的选型.WPF最吸引我的地方,就是MVVM模式了.MVVM模式完全把界面和业务 ...

  6. Mac安装mongodb并启动

    1.选择自己版本下载,下载完成后进入Finder看下usr文件夹下是否有mongodb2.默认情况下是看不到需要我们输入指令:shift + command +G 输入 /usr/local 进入文件 ...

  7. 超级简单的照片画廊MVC

    下载Gallery.zip - 23.5 MB 介绍 我想在我的个人网站上添加一个简单的图片库,但找不到任何合适的方法来从文件夹而不是数据库中挑选图片.也许我应该看得更仔细些!尽管如此,下面是我实现的 ...

  8. Redis 客户端 Jedis、lettuce 和 Redisson 对比

    Redis 支持多种语言的客户端,下面列举了部分 Redis 支持的客户端语言,大家可以通过官网查看 Redis 支持的客户端详情. C语言 C++ C# Java Python Node.js PH ...

  9. IDEA设置maven修改settings.xml配置文件无法加载仓库

    作为初学者配置maven一般网上搜索.然后你就看到各种配置文件片段,首先配置镜像,然后配置仓库.完事后再IDEA里面配置下maven的路径和配置文件路径. 这些文章属实坑爹,完全没讲一个重要的配置就是 ...

  10. 多测师讲解python __for 循环___高级讲师肖sir

    横向输出 1.遍历字符串 2.遍历列表 3.遍历元组 方法一: 方法二: 方法三: #循环字典:方法一# dict1={"name":"zhihao",&quo ...