3992: [SDOI2015]序列统计

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 1017  Solved: 466
[Submit][Status][Discuss]

Description

小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。

Input

一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。

Output

一行,一个整数,表示你求出的种类数mod 1004535809的值。

对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复


题意:有多少长为n的序列序列中每个元素属于S且乘积mod M=x


这道题太强啦

首先,发现m是一个质数,我们可以把乘法简化成加法

m的原根为g,让Si中元素取以g为底对模m的离散对数,记为ind[i]

因为g0,1,...,m-1  (mod m) 互不相同,所以可以把[1,m-1]的数字表示出来,并且它的取值为[0,m-2]

离散对数也满足一些类似对数的性质,如ind(ab)=ind(a)+ind(b) (mod m-1) 证明的话我从网上随便找了个课件

然后乘法就变成加法啦!

变成加法是为了用生成函数,系数是贡献,指数是选的个数

这是一个可重集的组合问题,我们构造一个生成函数A(x)  a[ind[si]]=1

AN的ind[x]项系数就是答案啦

如何计算呢?

因为这是模意义下的乘法,所以要用NNT

然后注意要求的是ind[si]也就是指数加起来mod(m-1) = ind[x],所以乘法结束时需要把>m-1的加到mod (m-1) 上并清零

然后用多项式快速幂,单位元就是a[0]=1

注意别把语句放错位置以及别传错参数......

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,m,x,S;
ll A[N],ans[N]; ll P=,MOD=P;
ll Pow(ll a,ll b,ll MOD){
ll ans=;
for(;b;b>>=,a=a*a%MOD)
if(b&) ans=ans*a%MOD;
return ans;
}
ll PrimitiveRoot(ll p){
if(p==) return ;
for(ll g=;g<p;g++){
bool flag=;ll m=sqrt(p);
for(ll i=;i<=m;i++) if((p-)%i==)
if(Pow(g,(p-)/i,p)==) {flag=;break;}
if(flag) return g;
}
return ;
}
int ind[N];
void iniInd(){
int g=PrimitiveRoot(m),a=;
for(int i=;i<m-;i++,a=a*g%m) ind[a]=i;
} struct NumberTheoreticTransform{
int n,rev[N];
ll g;
void ini(int m){
n=;
while(n<m) n<<=; int k=;
while((<<k)<n) k++;
for(int i=;i<n;i++){
int t=;
for(int j=;j<k;j++) if(i&(<<j)) t|=(<<(k-j-));
rev[i]=t;
} g=;
}
void DFT(ll *a,int flag){
for(int i=;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int l=;l<=n;l<<=){
int m=l>>;
ll wn=Pow(g,flag==?(P-)/l:P--(P-)/l,P);
for(ll *p=a;p!=a+n;p+=l){
ll w=;
for(int k=;k<m;k++){
ll t=w*p[k+m]%P;
p[k+m]=(p[k]-t+P)%P;
p[k]=(p[k]+t)%P;
w=w*wn%P;
}
}
}
if(flag==-){
ll inv=Pow(n,P-,P);;
for(int i=;i<n;i++) a[i]=a[i]*inv%P;
}
}
void SQR(ll *A){
DFT(A,);
for(int i=;i<n;i++) A[i]=A[i]*A[i]%MOD;
DFT(A,-);
for(int i=;i<=m-;i++)
A[i]=(A[i]+A[i+m-])%MOD,A[i+m-]=;
}
ll C[N];
void MUL(ll *A,ll *B){
for(int i=;i<n;i++) C[i]=B[i];
DFT(A,);DFT(C,);
for(int i=;i<n;i++) A[i]=A[i]*C[i]%MOD;
DFT(A,-);
for(int i=;i<=m-;i++)
A[i]=(A[i]+A[i+m-])%MOD,A[i+m-]=;
}
void PowPoly(ll *A,int b,ll *ans){
ans[]=;
for(;b;b>>=,SQR(A))
if(b&) MUL(ans,A);
}
}fft; int main(){
freopen("in","r",stdin);
n=read();m=read();x=read();S=read();
fft.ini(m+m);
iniInd();
for(int i=;i<=S;i++){
int x=read();
if(x) A[ind[x]]=;
} fft.PowPoly(A,n,ans);
printf("%lld",ans[ind[x]]);
}

BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]的更多相关文章

  1. BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

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

  2. BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Statu ...

  3. [BZOJ 3992][SDOI2015]序列统计

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 2275  Solved: 1090[Submit][Stat ...

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

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 有转移次数.模M余数.方案数三个值,一看就是系数的地方放一个值.指数的地方放一个值.做 ...

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

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

  6. bzoj 3992: [SDOI2015]序列统计【原根+生成函数+NTT+快速幂】

    还是没有理解透原根--题目提示其实挺明显的,M是质数,然后1<=x<=M-1 这种计数就容易想到生成函数,但是生成函数是加法,而这里是乘法,所以要想办法变成加法 首先因为0和任何数乘都是0 ...

  7. BZOJ.3992.[SDOI2015]序列统计(DP NTT 原根)

    题目链接 \(Description\) 给定\(n,m,x\)和集合\(S\).求\(\prod_{i=1}^na_i\equiv x\ (mod\ m)\)的方案数.其中\(a_i\in S\). ...

  8. bzoj 3992: [SDOI2015]序列统计 NTT+原根

    今天开始学习丧心病狂的多项式qaq......    . code: #include <bits/stdc++.h> #define ll long long #define setIO ...

  9. 3992: [SDOI2015]序列统计

    3992: [SDOI2015]序列统计 链接 分析: 给定一个集和s,求多少个长度为n的序列,满足序列中每个数都属于s,并且所有数的乘积模m等于x. 设$f=\sum\limits_{i=0}^{n ...

随机推荐

  1. Linux磁盘使用过载

    系统出现磁盘占用达到100%导致系统崩溃 查看那些个磁盘使用达到100% 命令 df -h  我们发现是www目录使用达到100%,开始清理,首先查找大于100M的大文件 find / -size + ...

  2. 五 : springMVC拦截器

    springMVC拦截器的实现一般有两种方式 第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口 第二种方式是继承实现了HandlerInte ...

  3. html button 点击链接

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. Thinkphp 3.2中文章详情页的上一篇 下一篇文章功能

      额 简单2句话解释下 获取上一篇文章的原理,其实就是以当前文章的id为起点进行进行查询,例如id=5的文章 select * from article where (article_id<5 ...

  5. 【开发技术】Xcode3与xcode4.2模板对比(Xcode4.2开发之一些变化)

    Xcode3中IOS下的Application的模板如下: Navigation_Based Application OpenGL ES Application Tab Bar Application ...

  6. Java数据持久层框架 MyBatis之API学习二(入门)

    对于MyBatis的学习而言,最好去MyBatis的官方文档:http://www.mybatis.org/mybatis-3/zh/index.html 对于语言的学习而言,马上上手去编程,多多练习 ...

  7. start tomcat with debugging mode

    For this, you must run your application in debug mode, which requires below parameters. -Xdebug -Xru ...

  8. 【转】GPS基线解算模式

    GPS基线向量是利用2台或2台以上GPS接 收机所采集的同步观测数据形成的差分观测值,通过参数估计得方法所计算出的两两接收机间的三维坐标差.与常规地面测量中所测定的基线边长不同,基线向量是 既具有长度 ...

  9. 5分钟编写运行一个RChain合约

    今天介绍如何编写和测试一个RChain智能合约,Rholang的语法介绍在https://developer.rchain.coop/tutorial 1.安装docker 这个自己百度一下,安装都是 ...

  10. 123 A. Prime Permutation

    链接 http://codeforces.com/contest/123/problem/A 题目 You are given a string s, consisting of small Lati ...