洛谷P1414 又是毕业季II
数论
d[ i ] 表示这些数中有几个数有因数 i
对于输入的每个数 都sqrt(val) 记录下
然后问你 n个数的最大公约数是多少,
相当于是问你 有 n 个数 有相同因数 ,这样最大的因数是多少
《又是毕业季II》解题报告
By lzn 数论常规题。
一开始很容易想到枚举n个数取k个的所有组合,然后分别用辗转相除法求最大公约数,但是复杂度明显不符合要求,于是必须换一种思路。
我们想到,k个数的公约数含义就是这k个数均含有某个因数,如果我们把所有数的因数全部求出来,发现有k个数均含有某个因数,那么这个数必然是这k个数的公约数。其中找出最大的就是它们的最大公约数。但是要如何高效的做到这点呢?考虑到对于k=1,2……,n都要求出,我们可以这么做:
1、 求出每个因数出现的次数。
2、 对于每个次数记录最大的因数。
3、 根据f[k]=max(f[k],f[k+1])逆向递推。(如果已经知道k个数的最大公约数是m,那么l(l<k)个数的最大公约数一定大于等于m)。
具体为什么这么做,留给大家自己思考啦~~
算法复杂度o(n*sqrt(inf))。
1 #include <cstdio> 2 #include <cmath> 3 #include <cstdlib> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 #include <iomanip> 9 using namespace std ; 10 11 const int maxn = 10011,inf = 1e6+11 ; 12 int n,mx,x,y ; 13 int d[inf] ; 14 15 inline int read() 16 { 17 char ch = getchar() ; 18 int x = 0,f = 1 ; 19 while(ch<'0'||ch>'9') { if(ch=='-') f = -1 ; ch = getchar() ; } 20 while(ch>='0'&&ch<='9') { x = x*10 + ch-48 ; ch = getchar() ; } 21 return x*f ; 22 } 23 24 int main() 25 { 26 n = read() ; 27 for(int i=1;i<=n;i++) 28 { 29 x = read() ; 30 mx = max(mx,x) ; 31 y = int(sqrt( x )+0.0001) ; 32 for(int j=1;j<=y;j++) 33 if(x%j==0) 34 { 35 d[ j ]++ ; 36 if(j*j!=x) d[ x/j ]++ ; 37 } 38 } 39 for(int i=1;i<=n;i++) 40 { 41 while(d[mx] < i ) mx-- ; 42 printf("%d ",mx) ; 43 } 44 return 0 ; 45 }