小B写了一个程序,随机生成了n个正整数,分别是a[1]..a[n],他取出了其中一些数,并把它们乘起来之后模p,得到了余数c。但是没过多久,小B就忘记了他选了哪些数,他想把所有可能的取数方案都找出来。你能帮他计算一下一共有多少种取数方案吗?请把最后的方案数模1000000007后输出。
小B记得他至少取了一个数。
对于30%的数据,n<=16;
另有30%的数据,p<=10000;
对于100%的数据,n<=32,p<=10^9,c<=10^9,a[i]
传说中的2^n算法能过30%.
另外百分之30考虑背包,用C作容量然后每一次取或不取
2^n大概能过n=20
考虑分治
先把n个数分成两组 (也就差不多一半,这个自己随便分,别超过20就好)
分开算每种余数方案数,用hash表存下 (STL黑科技map)
然后枚举一组的余数 假设是a
我们知道有ab=c(mod p)
转换为ab-kp=c用exgcd求解
解出b使得他在[0,p-1]这个区间内,这就是对应a的余数
然后将方案数一乘一加即可.
#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
long long n,na,p,c,ans,sox,soy;
map<long long,long long> ma,mb; //hashmap
long long a[100];
long long abs(long long x) {
return(x>0)?x:-x;
}
void dfs(int x,int ys) {
if (x>na) {
if (ys==c) ans++;
ma[ys]++;
return;
}
dfs(x+1,ys*a[x]%p);
dfs(x+1,ys);
}
void dfs2(int x,int ys) {
if (x>n) {
if (ys==c) ans++;
mb[ys]++;
return;
}
dfs2(x+1,ys*a[x]%p);
dfs2(x+1,ys);
}
long long gcd(long long a,long long b) {
return (b)?gcd(b,a%b):a;
}
void exgcd(long long a,long long b) {
if (b==0) {
sox=1;
soy=0;
return;
}
exgcd(b,a%b);
int tmp=sox;
sox=soy;
soy=tmp-a/b*soy;
}
int main() {
ma.clear();
mb.clear();
cin>>n>>p>>c;
if(c>=p) {
printf("0");
return 0;
}
na=n/2;
for (int i=1; i<=n; i++) cin>>a[i];
for (int i=1; i<=na; i++) dfs(i+1,a[i]);
for (int i=na+1; i<=n; i++) dfs2(i+1,a[i]);
map<long long,long long>::iterator it;
for (it=mb.begin(); it!=mb.end(); it++) {
int ys=it->first;
sox=soy=0;
exgcd(ys, p);
long long gc=gcd(ys,p);
if (c%gc!=0) continue;
sox=sox*(c/gc)%p;
if (sox<0) sox+=abs(sox)/p*p;;
if (sox<0) sox+=p;
while (sox<p) {
if (ma[sox]!=0) {
ans=(ans+ma[sox]*it->second)%1000000007;
}
sox+=p;
}
}
cout<<ans<<endl;
}