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 }
dfs代码

 

 

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 } 
组合计算

 

       

posted @ 2021-01-16 23:04  acmloser  阅读(105)  评论(0编辑  收藏  举报