zoukankan      html  css  js  c++  java
  • 简单数论 | 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.

  • 相关阅读:
    bzoj 1176 cdq分治套树状数组
    Codeforces 669E cdq分治
    Codeforces 1101D 点分治
    Codeforces 1100E 拓扑排序
    Codeforces 1188D Make Equal DP
    Codeforces 1188A 构造
    Codeforces 1188B 式子转化
    Codeforces 1188C DP 鸽巢原理
    Codeforces 1179D 树形DP 斜率优化
    git commit -m "XX"报错 pre -commit hook failed (add --no-verify to bypass)问题
  • 原文地址:https://www.cnblogs.com/izcat/p/9417020.html
Copyright © 2011-2022 走看看