桌面上有 n 张牌,每张牌上写了一个数字,第 i 张牌的数字为 ai 。现在从中选出 K 张牌,把选出牌上的数字全部乘起来,得到一个数 X。
问有多少种不同的选择方案,使得 X 和 A 的最大公约数大于等于 B。
数据范围:
第一行第 n , k , a ,b。接下来一行 n 个数,每张牌上的数字
输出方案数
5 2 12 6 4 4 1 2 3
3
#include <bits/stdc++.h> using namespace std; int gcd(int x, int y){ int t = x%y; while(t != 0){ x = y; y = t; t = x%y; } return y; } int main(){ int n, K, A, B; scanf("%d%d%d%d", &n, &K, &A, &B); long a[n], dp[100001][n+1]={0}; for(int i=0;i<n;i++) scanf("%ld", &a[i]); vector<int> v; for(int i=1;i*i<=A;i++){ if(A%i==0){ v.push_back(i); if(A/i != i) v.push_back(A/i); } } sort(v.begin(), v.end()); for(int i=0;i<n;i++) for(int j=K;j>0;j--){ if(j==1){ dp[gcd(a[i], A)][j] += 1; break; } for(auto &x: v) dp[gcd(a[i]*x, A)][j] += dp[x][j-1]; } long s = 0; for(int i=B;i<=A;i++) s += dp[i][K]; printf("%ld\n", s); return 0; }
思路:d[i][j][k]表示前i个数选j个数的乘积与A的GCD为k的方案数。
如果k达到100000,会MLE。怎么办呢?
由于A的因子个数最多也就100多个,所以A的GCD也就100多个。那么只要枚举A的因子数即可。
#include<bits/stdc++.h> using namespace std; const int MAX=2e5+10; typedef long long ll; ll d[2][51][10001]; int st[MAX]; vector<int>v; ll ***(ll x,ll y){return y==0?x:***(y,x%y);} int main() { int n,m,A,B; cin>>n>>m>>A>>B; for(int i=1;i*i<=A;i++) { if(A%i==0) { v.push_back(i); if(A/i!=i)v.push_back(A/i); } } sort(v.begin(),v.end()); for(int i=0;i<v.size();i++)st[v[i]]=i; memset(d,0,sizeof d); for(int i=1;i<=n;i++) { ll x; scanf("%lld",&x); d[i&1][1][st[***(x,A)]]=1; for(int j=1;j<=m;j++) { for(int k=0;k<v.size();k++) { if(d[(i&1)^1][j][k]==0)continue; if(j<m) { if(***(A,v[k]*x)<=B)d[i&1][j+1][st[***(A,v[k]*x)]]+=d[(i&1)^1][j][k]; else d[i&1][j+1][st[A]]+=d[(i&1)^1][j][k]; } d[i&1][j][k]+=d[(i&1)^1][j][k]; } } memset(d[(i&1)^1],0,sizeof d[(i&1)^1]); } ll ans=0; for(int i=0;i<v.size();i++) { if(v[i]>=B)ans+=d[n&1][m][i]; } printf("%lld\n",ans); return 0; }
//考虑递归+回溯取长度为k的所有组合 //考虑求当前组合的乘积对于A,B的最大公约数情况(直接循环相减至两个数相等) #include <vector> #include <algorithm> #include <iostream> using namespace std; class Solution{ public: vector<vector<int>> AllCombinations(vector<int>& vec,int K){ sort(vec.begin(),vec.end()); //重复元素,别忘记先sort+set限定唯一性//不需要set限定!!! dfs(vec,K,0); return res; } private: vector<vector<int>> res; vector<int> tmp; //set<vector<int>> S; //限定唯一性,一开始考虑错了,所以死活只有2,以为样例错了,在这里不需要唯一性 void dfs(vector<int>& vec,int K,int idx){ if((int)tmp.size()==K){ res.push_back(tmp); return; } for(int i=idx;i<(int)vec.size();++i){ tmp.push_back(vec[i]); dfs(vec,K,i+1); tmp.pop_back(); } } }; bool judge(vector<int>& nums,int A,int B){ long long X=1; for(auto num:nums) X*=num; //判定X和A的最大公约数 int target; if(X==A) target=X; else{ while(1){ if(X<A) A-=X; else if(X>A) X-=A; else{ target=X; break; } } } return target>=B; } int main(){ int n,K,A,B; cin>>n>>K>>A>>B; int idx=0; vector<int> vec(n); while(n--) cin>>vec[idx++]; //递归+回溯取所有可能 Solution sol; vector<vector<int>> res=sol.AllCombinations(vec,K); int cnt=0; //最终组合数目 for(int i=0;i<res.size();++i){ vector<int> cur=res[i]; //判定最大公约数 if(judge(cur,A,B)) cnt++; } cout<<cnt; return 0; }这是原来想的,给定的样例是过了,但是数据量大的时候超时错误,需要优化一下