zoukankan      html  css  js  c++  java
  • HDU4335 What is N? 欧拉函数,欧拉定理

    这题虽说解题报告中说的是简单题,但比赛中AC率以及提交量并不是很高,主要是数据范围太大,有点吓人,而且含有trick。

    题目给定b,p,M 问 0 <= n <= M 中有多少个数满足 n^(n!) ≡ b (MOD p),并且可恶的是这里没有提到任何数的特殊性质,给定都只是限定在正整数且p>0。

    有欧拉定理我们知道 n^(phi(p)) ≡ 1 (mod p) 但是这里要求gcd(n, p) = 1,显然题目并没有这么要的数据,那么如果题目给定是满足n,p互质的话,那么我们就可以知道
    n^(x) mod p 是有循环节的,这个循环节就是n^(phi(p)),如果n,p不互质的话,那么我们可以证明这个循环节 T | phi(p),所以我们还是可以选择phi(p)来作为循环节,于是
    下面我们就可以分四部分来进行计算了。

    part_1:首先计算n! < phi(p) 顶多8个点,for循环直接暴力

    part_2:如果还剩余有区间的话,在计算 phi(p) <= n! < s!, s=min{x|x! mod phi(p)=0} // 此时的n!次方已经存在一个循环节,但是n还是变化的

    part_3:接下来的区间就是n! % phi(p) = 0, 这说明其已经完全依赖于phi(p)的值了,所以只要考虑到n的循环性质了,直接统计n的一个循环节里面出现次数就可以了

    part_4:把余下的n进行统计即可

    代码如下:

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define MAXN 100000
    using namespace std;
    
    typedef unsigned long long int Int64;
    
    int p[MAXN+5], pm[10000], phi[MAXN+5], idx = -1, MOD, B;
    
    int Fac[MAXN+5];
    
    Int64 M, ans;
    
    void GetPrime()
    {
        for (int i = 2; i <= MAXN; ++i) {
            if (!p[i]) {
                pm[++idx] = i;                
            }
            for (int j = 0; pm[j]*i <= MAXN; ++j) {
                p[pm[j]*i] = 1;
                if (i % pm[j] == 0) {
                    break;    
                }
            }
        }
    /*    printf("%d\n", idx);
        for (int i = 0; i <= 100; ++i) {
            printf("%d ", pm[i]);
        }*/
    }
    
    void Eular()
    {
        phi[1] = 1;
        for (int i = 2; i <= MAXN; ++i) {
        //    printf("phi[%d] = %d\n", i-1, phi[i-1]);
        //    getchar();
            if (!p[i]) {
                phi[i] = i - 1;    
                continue;
            }
            for (int j = 0; pm[j]*pm[j] <= i; ++j) {
                if (i % pm[j] == 0) {
                    if (i / pm[j] % pm[j] == 0) {
                        phi[i] = pm[j] * phi[i/pm[j]];
                    }
                    else {
                        phi[i] = phi[pm[j]] * phi[i/pm[j]];
                    }
                    break;
                }
            }
            
        }
    }
    
    bool _pow(Int64 a, Int64 b)
    {
        Int64 ret = 1;
        while (b) {
            if (b & 1) {
                ret *= a;
                ret %= MOD;
            }
            a *= a;
            a %= MOD;
            b >>= 1;
        }
        return ret == B;
    }
    
    void deal()
    {
        int LIM = -1, ptr;
        for (ptr = 0; ptr <= M; ++ptr) {
            // 第一段,小于phi[MOD]的一部分 
            if (!ptr) { Fac[ptr] = 1; }
            else { Fac[ptr] = Fac[ptr-1] * ptr; }
            if (Fac[ptr] >= phi[MOD]) {
                break;
            }
            if (_pow(ptr, Fac[ptr])) {
                ++ans;
            }
        }
        if (ptr > M) { return; } // 如果已经超过了M的话 
        for (int i = ptr; i <= M; ++i) {
            if (!i) { Fac[i] = 1 % phi[MOD]; }
            else { Fac[i] = (Fac[i-1] * i) % phi[MOD]; }
            if (Fac[i] == 0) {  // 如果找到了这个点 
                LIM = i;
                break;
            }
            if (_pow(i, Fac[i]+phi[MOD])) {
                ++ans;
            }
        }
        if (LIM == -1) { return; } // 如果没有找到一个是phi[MOD]倍数的点 
        int t = 0;
        if ((M - LIM+1) >= MOD) { // LIM 这个点是没有计算的,所以长度为M-LIM+1,这里判定剩余是否大于MOD 
            for (int i = LIM; i < LIM+MOD; ++i) {
                if (_pow(i%MOD, phi[MOD])) { // 由于此时已经是phi[MOD]的倍数,所以直接把phi[MOD]这个指数代入
                    ++t;
                }
            }
            ans += (M - LIM+1) / (1LLU*MOD) * t;
        }
        int left = (M-LIM+1) % MOD; // 残余的余数 
        for (int i = LIM; i < LIM+left; ++i) {
            if (_pow(i%MOD, phi[MOD])) {
                ++ans;
            }
        }
    }
    
    int main()
    {
        GetPrime();
        Eular();
        int T, ca = 0;
        scanf("%d", &T);
        while (T--) {
            ans = 0;
            scanf("%d %d %I64u", &B, &MOD, &M);
            if(M == 18446744073709551615LLU && MOD == 1 && B == 0) {
                // 由于b=0,p=1,所以可以直接得到M+1,由于溢出原因所以需要特判 
                printf("Case #%d: 18446744073709551616\n", ++ca);
            }
            else 
            {    
                deal(); 
                printf("Case #%d: %I64u\n", ++ca, ans);
            }
        }
        return 0;
    }
  • 相关阅读:
    【转】 C++模板详解
    【转】 memcmp源码实现
    【转】 C++的精髓——虚函数
    【转】 如何使用Valgrind memcheck工具进行C/C++的内存泄漏检测
    【转】 优秀代码所具备的5大品质
    爬取贴吧中的html,并保存到相对应的文件夹中
    urllib模块通过post请求获取数据
    django,uwsgi, nginx部署项目
    Django中Ajax处理
    Django中的session于cookie的用法
  • 原文地址:https://www.cnblogs.com/Lyush/p/2626292.html
Copyright © 2011-2022 走看看