• 简单数论 | Day3 部分题解


      A - The Euler function 来源:HDU 2824

        计算[a,b]区间内的整数的欧拉函数值,需要掌握单个欧拉函数和函数表的使用。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    const int MAX_N  = 3000010;
    typedef long long ll;
    int phi[MAX_N];
    // ll sum_phi[MAX_N];  若使用前缀和累加,会爆内存(MLE)
    
    void phi_table(int n)
    {  // 计算得到n以内的欧拉函数表,参考蓝书P121
        int i, j;
        for(i=1;i<=n;i++)
            phi[i] = i;
             
        for(i=2;i<=n;i+=2)
            phi[i] /= 2;
             
        for(i=3;i<=n;i+=2)
        {
            if(phi[i]==i)
            {
                for(j=i;j<=n;j+=i)
                    phi[j] = phi[j] / i * (i - 1);
            }
        }
    }
    
    int main()
    {
        phi_table(3000000);

    // for(int i=1;i<=3000000;i++)
    // sum_phi[i] = sum_phi[i-1] + phi[i];

    // int a, b;
    // while(cin>>a>>b)
    // printf("%lld ", sum_phi[b]-sum_phi[a-1]);

    int a, b;
        while(cin>>a>>b)
        {
            ll sum = 0;
            for(int i=a;i<=b;i++)
                sum += phi[i];
            printf("%lld
    ", sum);
        }
        return 0;
    }

        B - Divisors  来源:POJ 2992 

        求组合数C(n, k)的因子个数,0 ≤ k ≤ n ≤ 431。

        打表可得431以内的素数只有83个,由于C(n, k) = n!/(k!*(n-k)!) = n*(n-1)*…*(n-k+1)/k!,开始的直观想法是求出分子上的每个素数因子的总个数,再减去分母上出现的每个素数因子的总个数,正约数的个数即为(a1+1)*(a2+1)*…*(an+1)。然而多次提交优化仍然超时。。。最后参考https://www.cnblogs.com/zxhyxiao/p/8026280.html,学到了计算N!中某个素因子个数的计算方法,结合网上的做法,终于AC。

    求N!中素因子p的个数,也就是N!中p的幂次

    公式为:cnt=[n/p]+[n/p^2]+[n/p^3]+...+[n/p^k]

    核心代码:

    int cnt = 0;
    while(N)
    {   cnt += N/p;   N /= p; }
     
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    int p[200], cnt, num[440];
    bool notprime[440];
    
    void prime_table(int n)
    { // 筛素数
      notprime[0] = notprime[1] = 1;
      int i, j;
      for(i=2;i<=n;i++)
      {
        if(!notprime[i])  
               {
                    num[i] = cnt;
                    p[cnt++] = i;
               }  
    
        for(j=0;j<cnt && i*p[j]<n;j++)
        {
          notprime[i*p[j]] = 1;
          if(i%p[j]==0) break;
        }
      }
    }
    
    
    int main()
    {
        
        prime_table(431);
    //    for(int i=0;i<cnt;i++) cout<<p[i]<<endl;
    //    cout<<cnt<<endl;
        int n, k;
        while(scanf("%d %d", &n, &k)!=EOF)
        {
            long long ans = 1;
            for(int i=0;i<cnt && p[i]<=n;i++)
            {
                int N = n, cnt = 0;
                while(N)
                {
                    cnt += N/p[i];
                    N /= p[i];
                }
                N = k;
                while(N)
                {
                    cnt -= N/p[i];
                    N /= p[i];
                }
                N = n-k;
                while(N)
                {
                    cnt -= N/p[i];
                    N /= p[i];
                }
                ans *= (cnt+1);
            }
            printf("%lld
    ", ans);
        } 
        return 0;
    }

        C - Longge's problem 来源:POJ 2480

        计算∑gcd(i, N), 1<=i<=N

        简单推导可得所求结果为∑i*phi(n/i), i|n.由于n的上限太大(1e18),书上打表的办法失效,一时陷入困境。再次百度参考https://www.cnblogs.com/flipped/p/5690123.html才茅塞顿开,直接采用O(√n)求单个欧拉函数值。需要特别注意注释处,最开始好几次TLE都是因为i*i结果为int类型溢出变为负数,出现了死循环。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    typedef long long ll;
    
    ll euler(int x)
    {  // 计算欧拉函数phi(x)
        int res=x, m = (int)sqrt(x+0.5);
        for(int i=2; i<=m; i++)
            if(x%i==0)
            {
                res = res/i*(i-1);
                while(x%i==0) x/=i;
            }
        if(x>1) res = res/x*(x-1);
        return res;
    }
    
    int main()
    {
        ll n, ans;
        while(scanf("%lld", &n)!=EOF)
        {
            ans = 0;
            int i;
            for(i=1;i<n/i;i++)  // for(i=1;(ll)i*i<n;i++) 
            {
                if(n%i==0)  ans += i*euler(n/i) + n/i*euler(i);
            }
            if(i*i==n)  ans += i*euler(i);
            printf("%lld
    ", ans);
        }
    }

        E - Happy 2006 来源:POJ 2773 

        计算与m互质的第K大的正整数,m (1 <= m <= 1000000), K (1 <= K <= 100000000)

        直接上AC代码:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    int gcd(int a,int b)
    {
        if(!b) return a;
        return gcd(b,a%b);
    }
    
    int euler(int x)
    {
        int res=x, m = (int)sqrt(x+0.5);
        for(int i=2; i<=m; i++)
            if(x%i==0)
            {
                res = res/i*(i-1);
                while(x%i==0) x/=i;
            }
        if(x>1) res = res/x*(x-1);
        return res;
    }
    
    int main()
    {
        int m, k;
        while(scanf("%d %d", &m, &k)!=EOF)
        {
            int sum = euler(m);
            int t = k % sum, tt = 0, i;
            if(t==0) t = sum;   //注意整除的处理
            for(i=1;i<m;i++)
            {
                if(gcd(i, m)==1) tt++;
                if(tt==t)  break;
            }
    
            printf("%d
    ", i+(k-1)/sum*m);
        }
    }

      貌似以上做法的欧拉函数显得多余,直接用gcd判断m以内全部互质的整数,总个数即为sum。

        D - GCD & LCM Inverse  来源:POJ 2429 

        最后AC的D题,参考了网上用dfs得到数组中最接近sqrt(N)的部分元素之积。晚上讲题解时滕佬指明一定要用Miller Rabin算法,直接在其模板基础上修改而来。代码十分长而且显得有些杂乱,日后再做整理吧~~

    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h> 
    #include <cmath>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    ll box[1000];
    ll N, T, num;
    
    ll min(ll a , ll b)
    {
        return (a < b) ? a : b;
    }
    
    ll gcd(ll a , ll b)
    {
        while(b)
        {
            ll t = a;
            a = b;
            b = t % b;
        }
            
        return a;
    }
    
    ll tim(ll a , ll b , ll mo)
    {
        a %= mo;
        b %= mo;
        
        ll ans = 0;
        
        while(b)
        {
            if(b & 1)
            {
                ans += a;
                if(ans > mo)
                    ans -= mo;
            }
                
            b >>= 1;
            a <<= 1;
            
            if(a > mo)
                a -= mo;
        }
        
        return ans;
    } 
    
    ll po(ll x , ll n , ll mo)
    {
        x %= mo;
        ll ans = 1;
        
        while(n)
        {
            if(n & 1)
                ans = tim(ans , x , mo);
            
            x = tim(x , x , mo);
            n >>= 1;
        }
        
        return ans;
    }
    
    bool tes(ll n , ll d , ll t , ll mo)
    {
        
        ll y = po(t , n , mo);
            
        ll i;
        for(i = 0 ; i < d ; i++)
        {
            ll no = tim(y , y , mo);
            
            if(y != mo - 1 && y != 1 && no == 1)
            {
                return false;
            }
                
            y = no;
        }
        
        if(y != 1)
        {
            return false;
        }
        
        return true;
    }
    
    bool MR(ll n)
    {
        if(n < 2)
            return false;
        
        if(n == 2 || n == 3)
            return true;
            
        if((n & 1) == 0 || n % 3 == 0)
            return false;
            
        ll b = n - 1, t = 0;
        
        while((b & 1) == 0)
        {
            b >>= 1;
            t++;
        }
        
        ll i, ty[8] = {2 , 3 , 7 , 61 , 24251 , 657 , 6436 , 123};
        
        for(i = 0 ; i < 8 ; i++)
        {
            if(!tes(b , t , ty[i] , n) && ty[i] < n)
            {
                return false;
            }
        }
        
        return true;
    }
    
    ll Ro(ll n , ll c)
    {
        srand(time(NULL));
        ll x0 = rand() % (n - 1) + 1;
        
        ll x = x0, y = x0, k = 0, i = 1, d = 1;
        
        while(d == 1)
        {
            k++;
            x = (tim(x , x , n) + c) % n;
            
            d = gcd(-1 * min(x - y , y - x) , n);
            
            if(d > 1 && d < n)
                return d;
            
            if(d == n)
                return -1;
                
            if(k == i)
            {
                y = x;
                i <<= 1;
            }
        }
    }
    
    void fj(ll n)
    {
        if(n == 1)
            return;
        
        if(MR(n))
        {
            box[num++] = n;
            return;
        }
        
        ll p = -1, c = 107;
        
        while(p == -1)
            p = Ro(n , c--);
    
        fj(n / p);
        fj(p);
    }
    
    ll fact[100], common;
    int cnt = 0;
    void dfs(ll now, int n)
    {
        if(now>sqrt(N))  return;
        common = max(common, now);
        
        for(int i=n;i<=cnt;i++)  dfs(now*fact[i], i+1);
    }
    
    int main()
    {
        
        ll lcm, gcd;
        while(scanf("%lld %lld", &gcd, &lcm)!=EOF)
        { 
            N = lcm / gcd;
    
            MR(N);
            num = 0;
            fj(N);
            
            sort(box, box+num);
    //        for(int i=0;i<num;i++)
    //            printf("%lld
    ", box[i]);
                
            cnt = 0;
            for(int i=1;i<100;i++)  fact[i] = 1;
            fact[0] = box[0];
            for(int i=1;i<num;i++)
            {
                if(box[i]==box[i-1])  fact[cnt] *= box[i];
                else  fact[++cnt] = box[i];
            }
            //for(int i=0;i<=cnt;i++)  printf("%lld
    ", fact[i]);
            
            common = 1; 
            dfs(1, 0);
            printf("%lld %lld
    ", common*gcd, lcm/common*gcd);
        }
        
        return 0;
    }
    View Code

        

       END.

  • 相关阅读:
    vim 编辑器
    用得到到的超方便软件
    关于 lua table表
    python 写入文件形式
    ifconfig中lo、eth0、br0、wlan0接口
    CPU:chip、core 和 processor 的关系
    lua 匹配空白符
    排序,查找(借鉴某大神的想法)
    sublime 快捷键
    获取进程的名称
  • 原文地址:https://www.cnblogs.com/izcat/p/9417020.html
Copyright © 2020-2023  润新知