bzoj4818 [Sdoi2017]序列计数
Description
Input
Output
Sample Input
Sample Output
正解:矩阵快速幂/$FFT$+快速幂+中国剩余定理。
昨天晚上考这题,发现是一道$FFT$优化$DP$的裸题。然而数组开小了,所以只有$80$分。。
正解似乎是矩阵快速幂,不过为什么$FFT$跑得快一些。。并且当$p$很大时矩阵快速幂就没用了。。
首先我们可以想到一个$O(n^{3})$的暴力$DP$。设$f[i][j]$表示前$i$个数,模$p$为$j$的方案数,那么$f[i][(j+k) \mod p]+=f[i-1][j]$,其中$k$为枚举选哪个数。
同正解一样,我们求出所有数的情况,然后减去没有质数的情况,最后得到的就是至少有一个质数的情况。
显然,这是一个卷积的形式,那么我们可以考虑用$FFT$来优化$DP$。
首先构造一个模$p$的多项式,$a[i]$表示模$p$为$i$的数有多少个。那么直接用$FFT$和快速幂算出$a^{n}$就行了。
然后线性筛求出所有质数,构造出除去所有质数的多项式$a$,再用一次$FFT$算出$a^{n}$。
第一个多项式的$a[0]$就是模$p$为$0$的情况,第二个多项式的$a[0]$就是没有任何质数且模$p$为$0$的情况,易知两个$a[0]$相减即为答案。
不过这个模数会炸精度,我们把这个模数拆成$8,1091,2311$,分别算出模这$3$个数的答案,最后用中国剩余定理合并就行了。
总复杂度$O(plogplogn)$,所以比矩阵快速幂的$O(p^{3}logn)$要快。不过我没加任何常数优化,所以还是很慢。。
//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define rhl (20170408)
#define NN (20000010)
#define pi acos(-1.0)
#define inf (1<<30)
#define il inline
#define RG register
#define ll long long
#define C complex <long double>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace std; C a[],b[],c[],ans[]; int prime[NN],rev[],r64[],N,n,m,p,lg,cnt;
ll res[],Ans[],ans1,ans2,aans;
bool vis[NN]; il int gi(){
RG int x=,q=; RG char ch=getchar();
while ((ch<'' || ch>'') && ch!='-') ch=getchar();
if (ch=='-') q=-,ch=getchar();
while (ch>='' && ch<='') x=x*+ch-,ch=getchar();
return q*x;
} il void sieve(){
vis[]=;
for (RG int i=;i<=m;++i){
if (!vis[i]) prime[++cnt]=i;
for (RG int j=,k;j<=cnt;++j){
k=i*prime[j]; if (k>m) break;
vis[k]=; if (i%prime[j]==) break;
}
}
return;
} il void FFT(C *a,RG int n,RG int f){
for (RG int i=;i<n;++i)
if (i<rev[i]) swap(a[i],a[rev[i]]);
for (RG int i=;i<n;i<<=){
C wn(cos(pi/i),sin(f*pi/i)),x,y;
for (RG int j=;j<n;j+=(i<<)){
C w(,);
for (RG int k=;k<i;++k,w*=wn){
x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y,a[j+k+i]=x-y;
}
}
}
return;
} il void mul(C *a,C *b,RG int pp){
for (RG int i=;i<N;++i) c[i]=b[i]; FFT(a,N,),FFT(c,N,);
for (RG int i=;i<N;++i) a[i]*=c[i]; FFT(a,N,-);
memset(res,,sizeof(res));
for (RG int i=;i<N;++i){
res[i%p]+=(ll)(a[i].real()/N+0.5);
res[i%p]%=pp,a[i]=;
}
for (RG int i=;i<p;++i) a[i]=res[i]; return;
} il void qpow(C *a,RG int b,RG int pp){
for (RG int i=;i<N;++i) ans[i]=a[i]; b--;
while (b){ if (b&) mul(ans,a,pp); mul(a,a,pp),b>>=; }
memset(res,,sizeof(res));
for (RG int i=;i<N;++i)
res[i%p]+=(ll)ans[i].real(),res[i%p]%=pp;
return;
} il void exgcd(RG ll a,RG ll b,RG ll &x,RG ll &y){
if (!b){ x=,y=; return; }
exgcd(b,a%b,y,x); y-=(a/b)*x; return;
} il void work(){
n=gi(),m=gi(),p=gi(); for (N=;N<=(p<<);N<<=) lg++;
for (RG int i=;i<N;++i) rev[i]=rev[i>>]>>|((i&)<<(lg-));
r64[]=,r64[]=,r64[]=,sieve();
for (RG int k=;k<=;++k){
memset(a,,sizeof(a)); for (RG int i=;i<=m;++i) a[i%p].real()++;
qpow(a,n,r64[k]); ans1=res[],memset(a,,sizeof(a));
for (RG int i=;i<=m;++i) if (vis[i]) a[i%p].real()++;
qpow(a,n,r64[k]); ans2=res[],Ans[k]=(ans1-ans2+r64[k])%r64[k];
RG ll u=rhl/r64[k],v=r64[k],x=,y=; exgcd(u,v,x,y);
aans+=u*x%rhl*Ans[k],aans%=rhl;
}
printf("%lld",aans); return;
} int main(){
File("count");
work();
return ;
}
bzoj4818 [Sdoi2017]序列计数的更多相关文章
- [BZOJ4818][SDOI2017]序列计数(动规+快速幂)
4818: [Sdoi2017]序列计数 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 972 Solved: 581[Submit][Status ...
- [bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛
[Sdoi2017]序列计数 题目大意:https://www.lydsy.com/JudgeOnline/problem.php?id=4818. 题解: 首先列出来一个递推式子 $f[i][0]$ ...
- 2019.02.11 bzoj4818: [Sdoi2017]序列计数(矩阵快速幂优化dp)
传送门 题意简述:问有多少长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数,且其中至少有一个数是质数,答案对201704082017040820170408取模(n≤1e9, ...
- BZOJ4818 [SDOI2017]序列计数 【生成函数 + 快速幂】
题目 Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数中,至少有一个数是质数.Alice想知道,有多少个序列满足她的要求. ...
- BZOJ4818 [SDOI2017] 序列计数 【矩阵快速幂】
题目分析: 一个很显然的同类项合并.注意到p的大小最大为100,考虑把模p意义下相同的求出来最后所有的减去没有质数的做矩阵快速幂即可. 代码: #include<bits/stdc++.h> ...
- 【BZOJ4818】[Sdoi2017]序列计数 DP+矩阵乘法
[BZOJ4818][Sdoi2017]序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数 ...
- BZOJ4818 LOJ2002 SDOI2017 序列计数 【矩阵快速幂优化DP】*
BZOJ4818 LOJ2002 SDOI2017 序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数. Alice还希 ...
- [Sdoi2017]序列计数 [矩阵快速幂]
[Sdoi2017]序列计数 题意:长为\(n \le 10^9\)由不超过\(m \le 2 \cdot 10^7\)的正整数构成的和为\(t\le 100\)的倍数且至少有一个质数的序列个数 总- ...
- BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法
BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ...
随机推荐
- 将两个列不同的DataTable合并成一个新的DataTable
/// <summary> /// 将两个列不同(结构不同)的DataTable合并成一个新的DataTable /// </summary> ...
- canvas基础—图形变换
1.canvas转换方法 1.1canvas转换方法 二.canvas实现图形的中心点旋转 step1:获取canva元素并指定canvas的绘图环境 var canvas=document.getE ...
- 基于ThinkPHP 5.0与Vue.JS 2.x的前后端开源开发框架VueThink
VueThink 项目介绍 VueThink是一套基于Vue全家桶(Vue2.x + Vue-router2.x + Vuex)+ Thinkphp的前后端分离框架. 脚手架构建也可以通过vue官方的 ...
- Linux学习之sudo命令
在学习Linux用户管理时,我们不得不需要了解一个命令,那就是sudo.sudo的作用是切换身份,以其他身份来执行命令. 那么为什么在Linux系统中我们需要来切换身份呢?原因有以下几个方面 1.养成 ...
- 【2017-03-30】JS-document对象
一.获取标记对象 1.document.getElementById("id"); 根据id找,最多找到一个. 2.document.getElementsByNa ...
- js前端获取页面传递的参数
转载的,先记下: UrlParm = function () { // url参数 var data, index; (function init() { data = []; index = {}; ...
- iOS 中的单例设计模式
单例设计模式:在它的核心结构中只包含一个被称为单例类的特殊类.例如文件管理中的NSUserDefault,应用程序中的UIApplication,整个应用程序就这一个单例类,负责应用程序的一些操作,单 ...
- Python之路-Linux命令基础(2)
作业一: 1) 新建用户natasha,uid为1000,gid为555,备注信息为"master" 2) 修改natasha用户的家目录为/Natasha 3) ...
- POPTEST老李谈钩子
poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...
- Java环境----JDK开发环境搭建及环境变量配置
1.啥是jdk? jdk的是java development kit的缩写,意思是java程序开发的工具包. jdk的版本,一共有三种,标准版(j2se),企业版(j2ee),移动设备版(j2me). ...