Day3 && Day4

本章内容对我来说真的是学的稀里糊涂的,除了前两题吭哧吭哧独立完成,第三题参考了别人的思路外,其余题目均是现学现卖,有点迷啊。所以写这篇博客的目的是先记录下聚聚们对本章内容相关重点的要求,并搜集一些相关资料,慢慢学吧。

D - GCD & LCM Inverse

题意:

给出两个整数 m、n ( < 2^63),求出两个整数 a、b ,使gcd(a, b)=m , lcm(a, b)=n ,并取 a + b 值最小的那一组。

Thinking:

拿到题看到数据范围后想了想没有思路。看了很多博客,对此题的化简分析有了认识,但实现本题所用的很多知识:

如:Miller-Rabin素性测试算法 和 进行素数分解的Pollard_Rho算法 却一头雾水。

在这里贴上一些博客供以后学习使用:
1、 https://blog.csdn.net/ECNU_LZJ/article/details/72675595

2、 https://blog.csdn.net/semiwaker/article/details/60142102

3 、https://blog.csdn.net/maxichu/article/details/45459533

4 、https://www.cnblogs.com/Norlan/p/5350243.html

5 、https://wenku.baidu.com/view/fbbed5a5f524ccbff12184af.html

通过化简使gcd(a1, b1)=1, 则 lcm(a1, b1)=n/m=s , 把s分解成两互质数s1, s2相乘的形式,这里由于n的范围过大,所以需要用到Miller_Rabin和Pollard_Rho算法将s分解。

要是a+b最小,则要使s1和s2越接近(初中的不等式知识可以证明),此处需要用到dfs()枚举s的num个互质的数如何组合成s1,s2并满足其差最小(这种枚举技巧学习了)。

(代码借鉴了很多前辈的,因为太多就不贴出处了,但还是要说声谢谢分享。)

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <cmath>
  4 #include <algorithm>
  5 using namespace std;
  6 #define LL long long
  7 
  8 LL qmul(LL a, LL b, LL c){
  9     a %= c;
 10     b %= c;
 11     LL res = 0;
 12     while(b){
 13         if(b&1){
 14             res = (res+a)%c;
 15         }
 16         a = (a+a)%c;
 17         b >>= 1;
 18     }
 19     return res;
 20 }
 21 LL qmodx(LL a, LL b, LL c){
 22     a %= c;
 23     b %= c;
 24     if(c <= 1000000000)
 25         return a * b % c;
 26     return (a * b - (LL)(a / (long double)c*b + 1e-8) *c + c) % c; 
 27 }
 28 LL qmod(LL a, LL b, LL c){
 29     LL res = 1;
 30     while(b){
 31         if(b&1){
 32             res = qmul(res, a, c);
 33         }
 34         b >>= 1;
 35         a = qmul(a, a, c)%c;
 36     }
 37     return res;
 38 }
 39 //以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数
 40 //一定是合数返回true,不一定返回false
 41 /*//////
 42 ????? 
 43 *////////
 44 bool check(LL a, LL n, LL x, LL t){
 45     LL ret = qmod(a, x, n);
 46     LL last = ret;
 47     for(int i=1; i<=t; i++){
 48         ret = qmul(ret, ret, n);
 49         if(ret == 1 && last!=1 && last!=(n-1))    return true;
 50         last = ret;
 51     }
 52     if(ret != 1)    return true;
 53     return false;
 54 }
 55 ////////----------
 56 // Miller_Rabin()算法素数判定
 57 //是素数返回true.(可能是伪素数,但概率极小)
 58 //合数返回false;
 59 bool Miller_Rabin(LL n, int S){
 60     if(n < 2)    return false;
 61     if(n == 2)    return true;
 62     if((n&1) == 0)    return false;
 63     LL x = n-1, t = 0;
 64     while((x&1) == 0){
 65         x >>= 1;
 66         t++;
 67     }
 68     for(int i=0; i<S; i++){
 69         LL a = rand()%(n-1) + 1;
 70         if(check(a, n, x, t)){
 71             return false;
 72         }
 73     }
 74     return true;
 75 }
 76 ///////////------------
 77 //************************************************
 78 //pollard_rho 算法进行质因数分解
 79 //************************************************
 80 ///////////----------
 81 int factor[1005];//质因数分解结果(刚返回时是无序的)
 82 int tol;//质因数的个数。数组小标从0开始
 83 LL gcd(LL a, LL b){
 84     if(a==0)    return 1;
 85     if(a < 0)    return gcd(-a, b);
 86     while(b){
 87         LL t = a % b;
 88         a = b;
 89         b = t;
 90     }
 91     return a;
 92 }
 93 LL Pollard_rho(LL x, LL c){
 94     LL i = 1, k = 2;
 95     LL x0 = rand()%x;
 96     LL y = x0;
 97     while(1){
 98         i++;
 99         x0 = (qmul(x0, x0, x) + c)%x;
100         LL d = gcd(y-x0, x);
101         if(d != 1 && d != x)    return d;
102         if(y == x0)        return x;
103         if(i == k){
104             y = x0;
105             k += k;
106         }
107     }
108 }
109 void findfac(LL n){
110     if(Miller_Rabin(n, 20)){
111         factor[tol++] = n;
112         return;
113     }
114     LL p = n;
115     while(p >= n){
116         p = Pollard_rho(p, rand()%(n-1)+1);
117     }
118     findfac(p);
119     findfac(n/p);
120 }
121 ///////---------
122 LL r[100];
123 int num;
124 LL k, sq;
125 void dfs(LL now, int x){
126     if( now > sq )    return;
127     k = max(k, now);
128     for(int i=x; i<=num; i++){
129         dfs(now*r[i], i+1);
130     }
131 }
132 
133 
134 int main(){
135     LL gc, lc, n;
136     while(scanf("%lld%lld", &gc, &lc) != EOF){
137         if(gc == lc){
138             printf("%lld %lld\n", gc, lc);
139             continue;
140         }
141         tol = 0;
142         n = lc/gc;
143         findfac(n);    sort(factor, factor + tol);
144         num = 0;
145         for(int i=0; i<=50; i++)    r[i] = 1;
146         r[num] = factor[0];
147         for(int i=1; i<tol; i++){
148             if(factor[i] == factor[i-1]){
149                 r[num] *= factor[i];
150             }else{
151                 r[++num] = factor[i];
152             }
153         }
154         k = 1;  sq = (LL)sqrt(1.0*n);
155         dfs(1, 0);
156         printf("%lld %lld\n", k*gc, lc/k);
157     }
158     return 0;
159 }

F - GCD

题意: 求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d)。注意[(x=5, y=7) and (x=7, y=5) are considered to be the same.]

听赵巨说此题有两种解法,可惜我都不会。

1、本题是一道莫比乌斯反演,就通过这道题了解并学习下。

同样贴出学习用到的博客地址:

  1. 初涉莫比乌斯反演
  2. 莫比乌斯反演详解
  3. 数论函数 - 莫比乌斯函数与莫比乌斯反演 - 基础杜教筛

下面给出两种mobius函数的求法:

 1 /*
 2 时间复杂度O(nlogn)
 3 这种筛法思路有点奇妙 
 4 */ 
 5 #include <cstdio>
 6 const int maxn = 100;
 7 int mu[maxn+20];
 8 void get_mu1(){
 9     for(int i=1;i<maxn;++i){
10         int delta = (i==1 ? 1-mu[i] : -mu[i]);
11         mu[i]=delta;
12         for(int j=2*i;j<N;j+=i)
13             mu[j]+=delta;
14     }
15 }
16 int main(){
17     get_mu1();
18     for(int i=0; i<maxn; i++){
19         printf("i = %d   mu[%d] = %d\n",i, i, mu[i]);
20     }
21     return 0;
22 }
View Code
 1 /*
 2 时间复杂度O(n)
 3 这种筛法思路有点像 线性素数筛
 4 去掉////则为线性素数筛法 
 5 */ 
 6 #include <cstdio>
 7 const int maxn = 100;
 8 int mu[maxn+20], p[maxn+20], vis[maxn+20];
 9 void get_mu2(int &cnt){
10     mu[1] = 1;
11     cnt = 0;
12     for(int i=2; i<maxn; i++){
13         if(!vis[i]){
14             p[cnt++] = i;
15             mu[i] = -1;   ////
16         }
17         for(int j=0; j<cnt && i*p[j]<maxn; j++){
18             vis[i*p[j]] = 1;
19             if(i % p[j]){  ////
20                 mu[i*p[j]] = -mu[i];
21             }else{
22                 mu[i*p[j]] = 0;  ////
23                 break;
24             }
25         }
26     }
27 }
28 int main(){
29     int cnt;
30     get_mu2(cnt);
31     for(int i=0; i<maxn; i++){
32         printf("i = %d   mu[%d] = %d\n",i, i, mu[i]);
33     }
34     return 0;
35 }
View Code

本题最重要的应该是构造出 f 和 F 两函数并找出其关系,然后通过约数莫比乌斯反演或倍数莫比乌斯反演的转换。

比较难思考的是构造……

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 #define LL long long
 5 const int maxn = 1e5+10;
 6 int mu[maxn+20], p[maxn+20], vis[maxn+20];
 7 void get_mu2(int &cnt){
 8     mu[1] = 1;
 9     cnt = 0;
10     for(int i=2; i<maxn; i++){
11         if(!vis[i]){
12             p[cnt++] = i;
13             mu[i] = -1; 
14         }
15         for(int j=0; j<cnt && i*p[j]<maxn; j++){
16             vis[i*p[j]] = 1;
17             if(i % p[j]){ 
18                 mu[i*p[j]] = -mu[i];
19             }else{
20                 mu[i*p[j]] = 0; 
21                 break;
22             }
23         }
24     }
25 }
26 int main(){
27     int cnt;
28     get_mu2(cnt);
29     int T, a, b, c, d, k;
30     scanf("%d", &T);
31     for(int t=1; t<=T; t++){
32         scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
33         printf("Case %d: ", t);
34         if(k == 0){
35             printf("0\n");
36             continue;
37         }
38         b /= k;    d /= k;
39         LL ans1 = 0, ans2 = 0;
40         int mi = min(b,d);
41         for(int i=1; i<=mi; i++){
42             ans1 += (LL)mu[i]*(b/i)*(d/i);
43         }
44         for(int i=1; i<=mi; i++){
45             ans2 += (LL)mu[i]*(mi/i)*(mi/i);
46         }
47         printf("%lld\n", ans1-ans2/2);
48     }
49     return 0;
50 }
View Code

2、可以用欧拉函数+容斥原理,将问题求(1,n)和(1,m)内素数对的问题转化为   (1,n)内欧拉函数和(的问题)与 (1,n)和(n+1,m)内素数对(的问题)。

        关于欧拉函数放上我学习过的文章:

  1.    欧拉函数求法与应用

第二个问题容斥原理有3种方法:队列数组   dfs   位运算

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 #define LL long long
  6 const int maxn = 10000;
  7 int prime[maxn+10];
  8 //线性素数筛法筛出素数表 
  9 void getprime(){  //prime[0]记录素数表的长度 
 10     memset(prime, 0, sizeof(prime));
 11     for(int i=2; i<=maxn; i++){
 12         if(!prime[i]){
 13             prime[++prime[0]] = i; 
 14         }
 15         for(int j=1; j<=prime[0] && prime[j]<=maxn/i; j++){
 16             //除法防溢出 
 17             prime[i*prime[j]] = 1;
 18             if(i%prime[j] == 0)    break;
 19         } 
 20     }
 21 }
 22 // 算术展开 
 23 LL factor[2][100]; //[0][i]:第i个素数值   [1][i]:第i个素数的个数 
 24 int fatcnt;
 25 void getfactors(LL x){
 26     fatcnt = 0;
 27     LL tmp = x;
 28     for(int i=1; prime[i] <= tmp/prime[i]; i++){
 29         factor[1][fatcnt] = 0;
 30         if(tmp % prime[i] == 0){
 31             factor[0][fatcnt] = prime[i];
 32             while(tmp % prime[i] == 0){
 33                 factor[1][fatcnt]++;
 34                 tmp /= prime[i];
 35             }
 36             fatcnt++;
 37         } 
 38     }
 39     if(tmp != 1){
 40         factor[0][fatcnt] = tmp;
 41         factor[1][fatcnt++] = 1;
 42     }
 43 }
 44 //欧拉函数打表 
 45 int euler[100010];
 46 void geteuler(){
 47     memset(euler, 0, sizeof(euler));
 48     euler[1] = 1;
 49     for(int i=2; i<=100000; i++){
 50         if(!euler[i]){
 51             for(int j=i; j <= 100000; j += i){
 52                 if(!euler[j]){
 53                     euler[j] = j;
 54                 }
 55                 euler[j] = euler[j]/i*(i-1);
 56             }
 57         }
 58     }
 59 }
 60 //用位运算实现(1,n)内和m互质的个数 ////n<m
 61 ///这里代码比较好理解,可以对比赵巨讲的那个2R/7的例子,或者容斥的定义来写 
 62 int calc(int n, int m){
 63     getfactors(m);
 64     int ans = 0;
 65     ///用二进制枚举m的几个素因子被用到 
 66     for(int i=1; i < (1<<fatcnt); i++){
 67         int cnt = 0;
 68         int tmp = 1;
 69         for(int j=0; j<fatcnt; j++){
 70             //目前第几个因子被使用 
 71             if(i & (1<<j)){
 72                 cnt++;
 73                 tmp *= factor[0][j];
 74             }
 75         }
 76         if(cnt & 1){
 77             ans += n/tmp;
 78         }else{
 79             ans -= n/tmp;
 80         }//容斥原理 
 81     }
 82     return (n - ans); 
 83 }
 84 int main(){
 85     getprime();
 86     geteuler();
 87     int a, b, c, d, k, T;
 88     scanf("%d", &T);
 89     for(int t=1; t<=T; t++){
 90         scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
 91         printf("Case %d: ", t);
 92         if(k==0 || k>b || k>d){
 93             printf("0\n");
 94             continue;
 95         }
 96         if(b > d)    swap(b,d);
 97         b /= k;  d /= k;
 98         LL ans = 0;
 99         for(int i=1; i<=b; i++){
100             ans += (LL)euler[i];
101         }
102         for(int i=b+1; i<=d; i++){
103             ans += (LL)calc(b, i);
104         }
105         printf("%lld\n", ans);
106     }
107     return 0;
108 } 

 G - Semi-prime H-numbers

题意:

定义一个数,称之为H-numbers,性质:除4余1;

数分三种:1、unit即1;       2、H-primes:满足H-numbers性质的素数;     3、剩下的是H-composite;

      H-semi-prime:两个H-prime相乘;

求h内有多少个H-semi-prime?

Thinking:

就按定义去筛出H-semi-prime数。

 1 #include <cstdio>
 2 #include <cstring>
 3 #define LL long long
 4 const int maxn = 1000001;
 5 int hprime[maxn+10];
 6 int vis[maxn+10];
 7 int hcnt;
 8 void getprime(){
 9     for(int i=5; i<=maxn; i+=4){
10         for(int j=5; (i*j<=maxn)&&(j<=maxn); j+=4){
11             if(!vis[i] && !vis[j]){
12                 vis[i*j] = 1;
13             }else{
14                 vis[i*j] = -1;
15                 //!(-1)==0
16             }
17         }
18     }
19     hcnt = 0;
20     for(int i=4; i<=maxn; i++){
21         if(vis[i] == 1){
22             hcnt++;
23         }
24         hprime[i] = hcnt;
25     }
26 }
27 int main(){
28     getprime();
29     int h;
30     while(scanf("%d", &h) && h){
31         printf("%d %d\n", h, hprime[h]);
32     }
33     return 0;
34 }

猜你喜欢

转载自www.cnblogs.com/seaupnice/p/9419836.html
今日推荐