数论部分补题的一点小收获

    连续5天的数学部分的知识学习已经结束了,今天本该好好消化吸收前4天所讲的知识点,最终却睡了一下午玩了一晚上。。。翻了翻早上半天时间补题的记录,还是总结一点东西吧,大部分还是昨天的研究成果。

    

    首先是K题:POJ 1284 Primitive Roots  题意很好理解,就是给定一个奇素数p,求它的原根个数。

     看懂了原根的定义以后,开始以为要一个一个检验计数,觉得太麻烦了,当然这个坑待填(求奇素数的最小原根)。这里讲清楚了原根个数的计算公式:一个整数m的原根个数为phi(phi(m))。特别地,p为素数时,phi(p)=p-1,那么p的原根个数就是phi(p-1)。那么这题就完结了。

    接下来说I题:POJ 3641 Pseudoprime numbers 伪素数——以我理解就是满足费马小定理的非素数。

 

    在维基百科欧拉函数的词条刚好看到了费马小定理的表述,这里先讲是因为昨天做这题刚好用错过。

    

    贴上部分代码说明:

bool isPrime(ll x)
{  // 判断是否时素数,配合素数打表使用
    if(x<=40000)  return !notprime[x];
    for(int i=0;i<cnt && primes[i]<=x/primes[i];i++)
    {
        if(x%primes[i]==0)
            return false;
    }
    return true;
}

ll pow_mod(ll a, ll n, ll mod)
{  // 快速幂取模
    ll ans = 1;
    a %= mod;
    while(n>0)
    {
        if(n&1)
        {
            ans *= a;
            ans %= mod;
        }
        a *= a%mod;
        a %= mod;
        n >>= 1;
    }
    return ans;

}

int main()
{  
    prime_table(40000);

    ll p, a;
    while(scanf("%lld %lld", &p, &a)!=EOF && a)
    {
        if(!isPrime(p) && pow_mod(a, p, p)==a%p)    //  WA : a^(p-1) == 1
            printf("yes\n");
        else
            printf("no\n");
    }

    return 0;
}

 

   由于a为正整数,gcd(a,p)不一定为1,所以利用费马小定理应该判断a^p%p是否等于a%p,而不是 a^(p-1)%p==1,我少算了1次幂以为能省时间反倒让我debug了好久。。。

 

    H题:POJ 3421 X-factor Chains 理解了题目意思后问题不大,很容易得到最长的X-链长度(素因子个数),就是被样例的数据误导了,走了些弯路探索排列总数有多少,其实就是简单的排列数问题。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int primes[1100001], cnt;
bool notprime[1100001];

void prime_table(int n)
{
    notprime[0] = notprime[1] = 1;
    for(int i=2;i<=n;i++)
    {
        if(!notprime[i])  
            primes[cnt++] = i;

        for(int j=0;j<cnt && i<=n/primes[j];j++)
        {
            notprime[i*primes[j]] = 1;
            if(i%primes[j] == 0) 
                break;
        }
    }
}

long long fact(int n)
{
    if(n==1||n==0)  return 1;
    return fact(n-1)*n;
}

long long res;
int diver(int n)
{
    int ans = 0;
    for(int i=0;i<cnt && n>=primes[i];i++)
    {
        if(!notprime[n])
        {
            ans++;
            break;
        }
        int t = 0;
        while(n%primes[i]==0)
        {
            n /= primes[i];
            t++;
        }
        res *= fact(t); 
        ans += t;

    }
    return ans;
} 

int main()
{
    prime_table(1100000);
    int x;
    while(scanf("%d", &x)!=EOF)
    {
        res = 1;
        int maxlen = diver(x);
        printf("%d %lld\n", maxlen, fact(maxlen)/res);
    }
}
View Code

 

    最后时G题:POJ 3292 Semi-prime H-numbers

    自己改造素数表的筛法经历了各种TLE和WA,参考了别人的代码才解决,折叠吧。大佬的代码在筛选的时候就标注了H-numbers的三种类型:prime/semi-prime/非semi-prime,避免了像我开始那样找到了筛选了全部的prime表后再次两两相乘组合,然后再将结果排序,最后线性查找给定的h前面有多少semi-prime。。。真是一把辛酸泪

#include <iostream>
#include <cstdio>
using namespace std;

const int maxn = 1000001;
int H[maxn+1], sum[maxn+1];
void solve()
{  // 类似筛法得到素数表
    for(int i=5;i<=maxn;i+=4)
        for(int j=5;j<=maxn && j<=maxn/i;j+=4)
        {  // H[i]==0 表示 i为H-prime

            if(!H[i]&&!H[j])
                H[i*j] = 1;  //标记是semi-prime
            else
                H[i*j] = -1;  //非semi-prime的合数

        }

    int cnt = 0;
    for(int i=1;i<=maxn;i++)
    {
        if(H[i]==1)  cnt++;
        sum[i] = cnt;
    }
}

int main()
{  
    solve();
    int h;
    while(scanf("%d", &h)!=EOF && h)
    {
        printf("%d %d\n", c, sum[h]);
    }

    return 0;
}
View Code

 

    今天还有好多知识点还没消化完,博客开始也一点不想记录,想了半天感觉一无所获0.0 所以标题还是夸大了点Orz

    最后还是提醒自己有空研究以下概念阶梯博弈莫比乌斯反演,然和利用莫比乌斯反演以及第一天PPT上所讲解决P题:HDU 5663 Hillan and the girl

    

 

    END.

 

posted @ 2018-08-06 01:45  izcat  阅读(347)  评论(0编辑  收藏  举报