[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 ...
随机推荐
- [转载]VS2010怎样打开VS2013或者VS2015建立的工程
VS2010怎样打开VS2013或者VS2015建立的工程 作用:解决vs低版本无法直接打开高版本的工程文件问题. 一.转载出处 http://blog.csdn.net/qq2399431200/a ...
- gradle本地、远程仓库配置--转
https://blog.csdn.net/x_iya/article/details/75040806 本地仓库配置配置环境变量GRADLE_USER_HOME,并指向你的一个本地目录,用来保存Gr ...
- angularjs ui-grid cellTemplate checkbox ng-checked
{ name: '@Localizer["ActiveInd"]', field: 'ActiveInd', enableSorting: false, ...
- 一个简单的json解析器
实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...
- File类的常用方法和练习
File类常用的判断方法 import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; /** * Fi ...
- PHP 经典算法
<? //-------------------- // 基本数据结构算法 //-------------------- //二分查找(数组里查找某个元素) function bin_s ...
- js中获取css样式的两种方式
1. 对象.style.样式名 弊端就是只能获取行内样式 2.window.getComputedStyle(对象,null); 最好用第二种方式 <!DOCTYPE html> < ...
- ES6学习笔记(二)-字符串的扩展
一.字符的 Unicode 表示法 JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点. 表示法只限于码点在\u0000~\uFFFF之间的字符, ...
- cf1060E. Sergey and Subway(树形dp)
题意 题目链接 Sol 很套路的题 直接考虑每个边的贡献,最后再把奇数点的贡献算上 #include<bits/stdc++.h> #define Pair pair<int, in ...
- encodeURIComponent() 函数的使用
说明:encodeURIComponent() 函数可把字符串作为 URI 组件进行编码. 维护项目中,遇到一个登录的问题:(用户的loginName为33195221,密码为147258369+), ...