AcWing 200. Hankson的趣味题
考察:dfs+质数筛+约数
错误思路:
枚举b1的每一个约数,进行gcd和lcm的判断
时间复杂度O(√10^9),再加上gcd和lcm的判断是log(n)=10,测试样例2000.时间复杂度>10^8,会有一个测试点TLE
注:gcd的时间复杂度是log(a+b)
正确思路:
同样需要枚举b1的每一个约数.但我们需要对求约数进行优化.已知b1可以分解为n个质因数.这n个质因数不同的指数可以取到不同的约数.
因此我们需要将b1分解质因数.但也不是从2开始枚举质因数.可以先预处理出√b以内的质数.再将其分解质因数. 时间复杂度大概是O(√10^9/ln√10^9)
1~N的质数个数是N/lnN
对于测试用例2000*O(之前算出的)*10(gcd和lcm) = 10^7
详细的证明在这位大佬的题解
代码:
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 using namespace std;//1.埃氏筛段错误 5 typedef long long ll; 6 typedef pair<int,int> pii; 7 const int N = 50010; 8 ll a0,a1,b0,b1; 9 int prime[N],cnt,cntf,work[N],cntd; 10 pii factor[N];//分解的质因数与其指数 11 bool st[N]; 12 void inits() 13 { 14 cnt = cntf = cntd = 0; 15 } 16 ll gcd(ll a,ll b) 17 { 18 return b?gcd(b,a%b):a; 19 } 20 ll lcm(ll a,ll b) 21 { 22 return a*b/gcd(a,b); 23 } 24 void GetPrime(int b) 25 { 26 for(int i=2;i<=b;i++) 27 { 28 if(!st[i]) prime[cnt++] = i; 29 for(int j=0;prime[j]<=b/i;j++) 30 { 31 st[i*prime[j]] = 1; 32 if(i%prime[j]==0) break; 33 } 34 } 35 } 36 void Getdivisor(ll b) 37 { 38 for(int i=0;prime[i]<=b/prime[i];i++) 39 { 40 if(b%prime[i]==0) 41 { 42 int s = 0; 43 while(b%prime[i]==0) 44 { 45 b/=prime[i]; 46 s++; 47 } 48 factor[cntf++] = {prime[i],s}; 49 } 50 } 51 if(b>1) factor[cntf++] = {b,1}; 52 } 53 ll Get(int a,int b) 54 { 55 ll res = 1; 56 while(b--) res*=a; 57 return res; 58 } 59 void dfs(ll tot,int pos) 60 { 61 if(pos>=cntf) 62 { 63 work[cntd++] = tot; 64 return; 65 } 66 for(int i=0;i<=factor[pos].second;i++) 67 { 68 ll tmp = Get(factor[pos].first,i); 69 dfs(tot*tmp,pos+1); 70 } 71 } 72 int main() 73 { 74 int T; 75 scanf("%d",&T); 76 GetPrime(N); 77 while(T--) 78 { 79 int ans = 0; 80 inits(); 81 scanf("%lld%lld%lld%lld",&a0,&a1,&b0,&b1); 82 Getdivisor(b1); 83 dfs(1,0); 84 for(int i=0;i<cntd;i++) 85 if(gcd(a0,work[i])==a1&&lcm(b0,work[i])==b1) ans++; 86 printf("%d\n",ans); 87 } 88 return 0; 89 }
2021.1.24 二刷,还是不会,这道题优化后的时间复杂度我反而算不明白了...
二刷后补了一个lyd大佬的超神解法,同样是对d分解质因数.求出a,b,c,d每个数对d的某一质因数d的分解个数.根据gcd和lcm的性质可以将x对此质数的分解个数确定.由此根据组合数乘法计算得到答案
时间复杂度O(5000T)
1 #include <iostream> 2 #include <algorithm> 3 #include <vector> 4 using namespace std; 5 typedef long long ll; 6 typedef pair<int,int> pii; 7 const int N = 50010; 8 int prime[N],cnt; 9 bool st[N]; 10 vector<pii> v; 11 void GetPrime(int n) 12 { 13 for(int i=2;i<=n;i++) 14 { 15 if(!st[i]) prime[++cnt] = i; 16 for(int j=1;prime[j]<=n/i;j++) 17 { 18 st[i*prime[j]] = 1; 19 if(i%prime[j]==0) break; 20 } 21 } 22 } 23 void GetDivide(ll n) 24 { 25 for(int i=1;prime[i]<=n/prime[i];i++) 26 { 27 if(n%prime[i]==0) 28 { 29 int s = 0; 30 while(n%prime[i]==0) s++,n/=prime[i]; 31 v.push_back({prime[i],s}); 32 } 33 } 34 if(n>1) v.push_back({n,1}); 35 } 36 int Getfactor(int p,ll a) 37 { 38 int s = 0; 39 while(a%p==0) a/=p,s++; 40 return s; 41 } 42 int main() 43 { 44 // freopen("in.txt","r",stdin); 45 int T; 46 scanf("%d",&T); 47 GetPrime(49000); 48 while(T--) 49 { 50 bool ok = 1; 51 ll a,b,c,d,res = 1; 52 scanf("%lld%lld%lld%lld",&a,&b,&c,&d); 53 v.clear(); 54 GetDivide(d); 55 for(int i=0;i<v.size();i++) 56 { 57 int p = v[i].first; 58 int ma = Getfactor(p,a); 59 int mb = Getfactor(p,b); 60 int mc = Getfactor(p,c); 61 int md = v[i].second; 62 if(ma<mb||mc>md) { ok=0;break; } 63 if(ma==mb&&mc==md&&mb<=md) res*=(ll)(md-mb+1); 64 else if(ma==mb&&mc<md&&mb<=md) res*=1; 65 else if(ma>mb&&mc==md&&mb<=md) res*=1; 66 else if(ma>mb&&mc<md&&mb==md) res*=1; 67 else { ok = 0; break; } 68 } 69 if(ok) printf("%lld\n",res); 70 else puts("0"); 71 } 72 return 0; 73 }