统计方案
小B写了一个程序,随机生成了n个正整数,分别是a[1]..a[n],他取出了其中一些数,并把它们乘起来之后模p,得到了余数c。但是没过多久,小B就忘记他选了哪些数,他想把所有可能的取数方案都找出来。你能帮他计算一下一共有多少种取数方案吗?请把最后的方案数模1000000007后输出。
小B记得他至少取了一个数。
输入格式:
第一行三个正整数n,p,c,含义如题目所述。接下来一行有n个正整数,表示生成的n个随机数。
输出格式:
一个数,方案数模1000000007。样例输入:
2 7 2
1 2
样例输出:
2
数据范围:
对于30%的数据,n ≤ 16另有30%的数据,p ≤ 10000
对于100%的数据,n ≤ 32, p ≤ 10^9, c ≤ 10^9, a[i] < p, p是质数
解题思路:
看到这种n <=32的数据范围,其实很多大佬都可以猜到这是中途相遇法
***如果知道这个的可以跳过下面这段介绍
中途相遇法:事实上就是进行两次dfs,在第一次dfs时记录下搜索到的结果,
一般用map或者hash,然后第二次是在dfs结束时根据上一次的结果,记录答案
***然后是对于这道题的介绍
你先dfs前(n/2)个数,选或不选,结束时记录这些数的乘积a,我是用map来记录的
然后在dfs后(n/2)个数,同样是选或不选,结束时得到这些数的乘积为b
因为a*b%p=c,所以我们可以用(c/b)在%p意义下的逆元就是a的值
最后再返回到原来的map里面找
如果没看懂,可以借助程序在思考一下
***注意:(1)因为不能不取,所以在c=1的时候,答案要减一
(2)c有可能大于等于p,这种情况直接输出0
%:pragma GCC optimize(2) #include<bits/stdc++.h> #define int long long #define czx 1000000007 using namespace std; int ans=0,a[50],n,p,c,m; map <int,int> mp; int pow(int a){ int res=1,b=p-2; while (b){ if (b&1) res=(res*a)%p; b/=2; a=(a*a)%p; } return res; } inline void dfs(int x,int s){ if (x==m+1){ mp[s]++; return; } dfs(x+1,s); dfs(x+1,1ll*s*a[x]%p); } inline void dfs1(int x,int s){ if (x==n+1){ int a=c*pow(s)%p; ans=(ans+mp[a])%czx; return; } dfs1(x+1,s); dfs1(x+1,1ll*s*a[x]%p); } main(){ scanf("%lld%lld%lld",&n,&p,&c); if (c>=p){ puts("0"); return 0; } for (int i=1;i<=n;++i) scanf("%lld",&a[i]); m=n/2; dfs(1,1); dfs1(m+1,1); if (c==1) ans--; printf("%lld",ans); }
最后再给上几道中途相遇法的题目