[BZOJ 3992][SDOI2015]序列统计
3992: [SDOI2015]序列统计
Time Limit: 30 Sec Memory Limit: 128 MB
Submit: 2275 Solved: 1090
[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中的所有元素。1<=N<=10^9,3<=M<=8000,M为质数0<=x<=M-1,输入数据保证集合S中元素不重复x∈[1,m-1]集合中的数∈[0,m-1]
Output
一行,一个整数,表示你求出的种类数mod 1004535809的值。
Sample Input
4 3 1 2
1 2Sample Output
8
【样例说明】
可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、
(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)
题解
这题的要求非常类似背包, 但是值之间做的贡献是乘积的形式. 我们考虑把它转成加法.
怎么转加法呢? 当然是取对数了!
离散对数哪家强? SDOI找...(划掉)
注意到如果数列里有 $0$ 那么贡献就是 $0$, 但是题目规定要求的目标值非 $0$, 所以我们首先先把 $0$ 扔掉
然后暴力计算出 $m$ 的一个原根, 同时处理出每个数的对数
那么这就是一个长度为 $m-1$ 的循环卷积辣!
然而循环卷积需要膜数有 $m-1$ 次单位根(吧), 于是只能NTT倍增爆算了...(或者博主naive?)
参考代码
多项式左右移操作现已加入豪华午餐
- #include <bits/stdc++.h>
- const int G=;
- const int DFT=;
- const int IDFT=-;
- const int MAXN=;
- const int MOD=;
- const int INV2=(MOD+)>>;
- const int PHI=MOD-;
- typedef std::vector<int> Poly;
- Poly Sqrt(Poly);
- void Read(Poly&);
- Poly Inverse(Poly);
- Poly Ln(const Poly&);
- Poly Exp(const Poly&);
- void Print(const Poly&);
- void NTT(Poly&,int,int);
- Poly Pow(const Poly&,int);
- Poly Integral(const Poly&);
- Poly Derivative(const Poly&);
- Poly operator*(Poly,Poly);
- Poly operator/(Poly,Poly);
- Poly operator%(Poly,Poly);
- Poly operator<<(const Poly&,int);
- Poly operator>>(const Poly&,int);
- Poly operator+(const Poly&,const Poly&);
- Poly operator-(const Poly&,const Poly&);
- int rev[MAXN];
- int FindG(int);
- int NTTPre(int);
- int Sqrt(int,int);
- int Pow(int,int,int);
- int Log(int,int,int);
- int ExGCD(int,int,int&,int&);
- int k,m,x,n;
- int lg[MAXN];
- int main(){
- scanf("%d%d%d%d",&k,&m,&x,&n);
- Poly a(m-);
- int g=FindG(m);
- for(int i=,pw=;i<m-;i++,pw=pw*g%m)
- lg[pw]=i;
- for(int i=;i<n;i++){
- int x;
- scanf("%d",&x);
- if(x)
- a[lg[x]]=;
- }
- --m;
- Poly ans(a);
- --k;
- while(k>){
- if(k&){
- ans=a*ans;
- ans=ans+(ans>>m);
- ans.resize(m);
- }
- a=a*a;
- a=a+(a>>m);
- a.resize(m);
- k>>=;
- }
- printf("%d\n",ans[lg[x]]);
- return ;
- }
- Poly operator<<(const Poly& a,int x){
- Poly ans(a.size()+x);
- for(size_t i=;i<a.size();i++)
- ans[i+x]=a[i];
- return ans;
- }
- Poly operator>>(const Poly& a,int x){
- Poly ans(a.size()-x);
- for(size_t i=x;i<a.size();i++)
- ans[i-x]=a[i];
- return ans;
- }
- int FindG(int mod){
- for(int g=;g<mod;g++){
- bool flag=true;
- for(int i=,pw=g;i<mod-;i++,pw=pw*g%mod){
- if(pw==){
- flag=false;
- break;
- }
- }
- if(flag)
- return g;
- }
- return -;
- }
- void Read(Poly& a){
- for(auto& i:a)
- scanf("%d",&i);
- }
- void Print(const Poly& a){
- for(auto i:a)
- printf("%d ",i);
- puts("");
- }
- Poly Pow(const Poly& a,int k){
- Poly log=Ln(a);
- for(auto& i:log)
- i=1ll*i*k%MOD;
- return Exp(log);
- }
- Poly Sqrt(Poly a){
- int len=a.size();
- if(len==)
- return Poly(,Sqrt(a[],MOD));
- else{
- Poly b=a;
- b.resize((len+)>>);
- b=Sqrt(b);
- b.resize(len);
- Poly inv=Inverse(b);
- int bln=NTTPre(inv.size()+a.size());
- NTT(a,bln,DFT);
- NTT(inv,bln,DFT);
- for(int i=;i<bln;i++)
- a[i]=1ll*a[i]*INV2%MOD*inv[i]%MOD;
- NTT(a,bln,IDFT);
- for(int i=;i<len;i++)
- b[i]=(1ll*b[i]*INV2%MOD+a[i])%MOD;
- return b;
- }
- }
- Poly Exp(const Poly& a){
- size_t len=;
- Poly ans(,),one(,);
- while(len<(a.size()<<)){
- len<<=;
- Poly b=a;
- b.resize(len);
- ans=ans*(one-Ln(ans)+b);
- ans.resize(len);
- }
- ans.resize(a.size());
- return ans;
- }
- Poly Ln(const Poly& a){
- Poly ans=Integral(Derivative(a)*Inverse(a));
- ans.resize(a.size());
- return ans;
- }
- Poly Integral(const Poly& a){
- int len=a.size();
- Poly ans(len+);
- for(int i=;i<len;i++)
- ans[i]=1ll*a[i-]*Pow(i,MOD-,MOD)%MOD;
- return ans;
- }
- Poly Derivative(const Poly& a){
- int len=a.size();
- Poly ans(len-);
- for(int i=;i<len;i++)
- ans[i-]=1ll*a[i]*i%MOD;
- return ans;
- }
- Poly operator/(Poly a,Poly b){
- int n=a.size()-,m=b.size()-;
- Poly ans();
- if(n>=m){
- std::reverse(a.begin(),a.end());
- std::reverse(b.begin(),b.end());
- b.resize(n-m+);
- ans=Inverse(b)*a;
- ans.resize(n-m+);
- std::reverse(ans.begin(),ans.end());
- }
- return ans;
- }
- Poly operator%(Poly a,Poly b){
- int n=a.size()-,m=b.size()-;
- Poly ans;
- if(n<m)
- ans=a;
- else
- ans=a-(a/b)*b;
- ans.resize(m);
- return ans;
- }
- Poly operator*(Poly a,Poly b){
- int len=a.size()+b.size()-;
- int bln=NTTPre(len);
- NTT(a,bln,DFT);
- NTT(b,bln,DFT);
- for(int i=;i<bln;i++)
- a[i]=1ll*a[i]*b[i]%MOD;
- NTT(a,bln,IDFT);
- a.resize(len);
- return a;
- }
- Poly operator+(const Poly& a,const Poly& b){
- Poly ans(std::max(a.size(),b.size()));
- std::copy(a.begin(),a.end(),ans.begin());
- for(size_t i=;i<b.size();i++)
- ans[i]=(ans[i]+b[i])%MOD;
- return ans;
- }
- Poly operator-(const Poly& a,const Poly& b){
- Poly ans(std::max(a.size(),b.size()));
- std::copy(a.begin(),a.end(),ans.begin());
- for(size_t i=;i<b.size();i++)
- ans[i]=(ans[i]+MOD-b[i])%MOD;
- return ans;
- }
- Poly Inverse(Poly a){
- int len=a.size();
- if(len==)
- return Poly(,Pow(a[],MOD-,MOD));
- else{
- Poly b(a);
- b.resize((len+)>>);
- b=Inverse(b);
- int bln=NTTPre(b.size()*+a.size());
- NTT(a,bln,DFT);
- NTT(b,bln,DFT);
- for(int i=;i<bln;i++)
- b[i]=(2ll*b[i]%MOD-1ll*b[i]*b[i]%MOD*a[i]%MOD+MOD)%MOD;
- NTT(b,bln,IDFT);
- b.resize(len);
- return b;
- }
- }
- void NTT(Poly& a,int len,int opt){
- a.resize(len);
- for(int i=;i<len;i++)
- if(rev[i]>i)
- std::swap(a[i],a[rev[i]]);
- for(int i=;i<len;i<<=){
- int step=i<<;
- int wn=Pow(G,(PHI+opt*PHI/step)%PHI,MOD);
- for(int j=;j<len;j+=step){
- int w=;
- for(int k=;k<i;k++,w=1ll*w*wn%MOD){
- int x=a[j+k];
- int y=1ll*a[j+k+i]*w%MOD;
- a[j+k]=(x+y)%MOD;
- a[j+k+i]=(x-y+MOD)%MOD;
- }
- }
- }
- if(opt==IDFT){
- int inv=Pow(len,MOD-,MOD);
- for(int i=;i<len;i++)
- a[i]=1ll*a[i]*inv%MOD;
- }
- }
- int NTTPre(int n){
- int bln=,bct=;
- while(bln<n){
- bln<<=;
- ++bct;
- }
- for(int i=;i<bln;i++)
- rev[i]=(rev[i>>]>>)|((i&)<<(bct-));
- return bln;
- }
- inline int Pow(int a,int n,int p){
- int ans=;
- while(n>){
- if(n&)
- ans=1ll*a*ans%p;
- a=1ll*a*a%p;
- n>>=;
- }
- return ans;
- }
- int ExGCD(int a,int b,int& x,int& y){
- if(b==){
- x=,y=;
- return a;
- }
- else{
- int gcd=ExGCD(b,a%b,y,x);
- y-=x*(a/b);
- return gcd;
- }
- }
- inline int Sqrt(int a,int p){
- int s=Pow(G,Log(G,a,p)>>,p);
- return std::min(s,MOD-s);
- }
- inline int Log(int a,int x,int p){
- int s=sqrt(p+0.5);
- int inv=Pow(Pow(a,s,p),p-,p);
- std::unordered_map<int,int> m;
- m[]=;
- int pow=;
- for(int i=;i<s;i++){
- pow=1ll*a*pow%p;
- if(!m.count(pow))
- m[pow]=i;
- }
- for(int i=;i<s;i++){
- if(m.count(x))
- return i*s+m[x];
- x=1ll*x*inv%MOD;
- }
- return -;
- }
BZOJ 3992
[BZOJ 3992][SDOI2015]序列统计的更多相关文章
- BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1017 Solved: 466[Submit][Statu ...
- BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1155 Solved: 532[Submit][Statu ...
- BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)
3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S ...
- bzoj 3992 [SDOI2015]序列统计——NTT(循环卷积&&快速幂)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 有转移次数.模M余数.方案数三个值,一看就是系数的地方放一个值.指数的地方放一个值.做 ...
- bzoj 3992 [SDOI2015] 序列统计 —— NTT (循环卷积+快速幂)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 (学习NTT:https://riteme.github.io/blog/2016-8 ...
- BZOJ.3992.[SDOI2015]序列统计(DP NTT 原根)
题目链接 \(Description\) 给定\(n,m,x\)和集合\(S\).求\(\prod_{i=1}^na_i\equiv x\ (mod\ m)\)的方案数.其中\(a_i\in S\). ...
- bzoj 3992: [SDOI2015]序列统计【原根+生成函数+NTT+快速幂】
还是没有理解透原根--题目提示其实挺明显的,M是质数,然后1<=x<=M-1 这种计数就容易想到生成函数,但是生成函数是加法,而这里是乘法,所以要想办法变成加法 首先因为0和任何数乘都是0 ...
- bzoj 3992: [SDOI2015]序列统计 NTT+原根
今天开始学习丧心病狂的多项式qaq...... . code: #include <bits/stdc++.h> #define ll long long #define setIO ...
- 3992: [SDOI2015]序列统计
3992: [SDOI2015]序列统计 链接 分析: 给定一个集和s,求多少个长度为n的序列,满足序列中每个数都属于s,并且所有数的乘积模m等于x. 设$f=\sum\limits_{i=0}^{n ...
随机推荐
- [转]MAC:删除终端默认前缀的计算机名
MAC:删除终端默认前缀的计算机名 1.打开终端 输入 sudo vi /etc/bashrc,提示输入密码就是计算机的密码. 2.点击i将编辑模式改成insert修改文档,使用#注释PS1=’\h: ...
- SqlHelper---操作数据库
public class SqlHelper { /// <summary> /// 数据库连接字符串 /// </summary> public static readonl ...
- JavaScript对象——原型与原型链
原型与原型链 一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function 是 JS 自带的函数对象.下面举例说明 va ...
- 第5章 scrapy爬取知名问答网站
第五章感觉是第四章的练习项目,无非就是多了一个模拟登录. 不分小节记录了,直接上知识点,可能比较乱. 1.常见的httpcode: 2.怎么找post参数? 先找到登录的页面,打开firebug,输入 ...
- 欢迎来到GIS思考者的博客www.gisthinker.com
我是一名GIS爱好者,这是我的个人博客,欢迎点击: GIS思考者:www.gisthinker.com
- TRUNCATE TABLE 与 DELETE的区别
delete from aatruncate table aa 区别1.delete from后面可以写条件,truncate不可以2.delete from记录是一条条删的,所删除的每行记录都会进日 ...
- Jquery Easy UI初步学习(一)
Easy UI 1.3.2 以前听说Easy UI很不错,当了一个dome,闲着没事就看了一下,也整理一下为了自己更好的记忆,也希望对象我这样小菜有帮助吧 先从后台管理的主页面开始,如要要做主页需要了 ...
- 实例-PHP_SELF、 SCRIPT_NAME、 REQUEST_URI区别-获取前台公用文-dirname-PHP的"魔术常量"-str_replace
Part1:实例 $_SERVER[PHP_SELF], $_SERVER[SCRIPT_NAME], $_SERVER['REQUEST_URI'] 在用法上是非常相似的,他们返回的都是与当前正在使 ...
- 前端(七):ES6一些新特性
一.变量 1.var关键字的弊端 var关键字的弊端:1.可以重复声明变量:2.无法限制变量修改:3.没有块级作用域,只有函数作用域. <html lang="en"> ...
- 【SSH网上商城项目实战19】订单信息的级联入库以及页面的缓存问题
转自: https://blog.csdn.net/eson_15/article/details/51433247 购物车这一块还剩最后两个问题,就是订单信息的级联入库和页面缓存,这里的 ...