[XJOI]noip40 T2统计方案

统计方案

小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);
}
View Code

最后再给上几道中途相遇法的题目

原文地址:https://www.cnblogs.com/logic-yzf/p/7576308.html