题意:
给定
n
n
n个数和一个数
k
k
k,问存在多少个子序列(不要求连续)满足序列所有数的乘积等于
k
k
k。
思路:
考虑
D
P
DP
DP
设 d p [ i ] dp[i] dp[i]为乘积为 i i i的子序列个数。
假设前 n − 1 n-1 n−1个数的子序列情况已经全部处理完毕,则对于第 n n n个数 a n a_n an:
如果
a
n
a_n
an不是
k
k
k的因子,则
a
n
a_n
an一定不可能对答案产生贡献。
否则:
$ans $
+
+
+
=
=
=
d
p
[
k
/
a
n
]
dp[k/a_n]
dp[k/an]
然后考虑 a n a_n an的贡献来更新 d p dp dp数组:
枚举
k
k
k的每个约数
x
x
x,若
x
∗
a
n
x*a_n
x∗an不是
k
k
k的约数,则不产生贡献,考虑下一个约数。
否则
d
p
[
x
∗
a
n
]
+
=
d
p
[
x
]
dp[x*a_n] += dp[x]
dp[x∗an]+=dp[x]
类似于01背包的转移方式。
因为 k k k很大,考虑离散化处理即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1
vector<int> vec;
const int mod = 1e9 + 7;
const int A = 1e3 + 10;
int n,k;
int dp[A];
void init(){
memset(dp,0,sizeof(dp));dp[1] = 1;
vec.clear();vec.push_back(0);
for(int i=1 ;i*i<=k ;i++){
if(k%i == 0){
vec.push_back(i);
if(i*i != k){
vec.push_back(k/i);
}
}
}
sort(vec.begin(),vec.end());
}
int get_id(int x){
return lower_bound(vec.begin(),vec.end(),x) - vec.begin();
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
init();
int ans = 0,ed = vec.size();
for(int i=1 ;i<=n ;i++){
int x;
scanf("%d",&x);
if(k%x) continue;
ans = (ans + dp[get_id(k/x)])%mod;
for(int j=ed-1 ;j>0 ;j--){
int now = x*vec[j];
if(k%now) continue;
dp[get_id(now)] = (dp[get_id(now)] + dp[j])%mod;
}
}
printf("%d\n",dp[ed-1]);
}
return 0;
}