暑训day3&4 数论呜呜呜呜

简直哭了~~~羡慕讲台上数学巨强的同级大佬
进入正题 呵~数论!
大佬“在线”辅导
一些基本概念(以下维基百科或百度即可)

  1. 素数增长趋势
  2. 同余
  3. 逆元
  4. 欧拉!(niupi)
  5. ······

一些基本定理及算法(以下维基百科或百度即可)

  1. 唯一分解定理(大部分后续公式基础)
  2. 欧几里得算法(gcd——最大公约数)
  3. 扩展欧几里得算法(ex_gcd)
  4. 各类模运算(注意除法取模求逆元)
  5. 模方程(中国剩余定理)
  6. 欧拉函数!!!
  7. 判断素数(素数打表)
  8. Laucs定理
  9. 威尔逊定理
  10. 费马小定理
  11. 莫比乌斯反演(不会)
  12. Baby step Giant step(不会)
  13. 各种筛(哭了)
  14. ······

只是初次学习嘛,两天掌握还是蛮困难的,就题说吧

A - The Euler function HDU - 2824
题目大意:求1-n的欧拉函数的和
欧拉函数的应用,只不过要找一个快速的算法进行预处理(否则每次计算单个欧拉函数会t)
核心代码(欧拉函数打表)

int phi[N];
void Euler(){
    phi[1] = 1;
    for(int i = 2; i < N; i ++){
        if(!phi[i]){
            for(int j = i; j < N; j += i){
                if(!phi[j]) phi[j] = j;
                phi[j] = phi[j] / i * (i-1);
            }
        }
    }
}

B - Divisors POJ - 2992
题目大意:给出n,k,计算 C n k ,并求该组合数求因子个数
以为先求组合数再用唯一分解定理求因子个数,结果为了求组合数不超时不溢出,特地搜到了一篇超牛掰的方法求组合数(大致思路为担心数据过大所以对组合数取对数······)。
结果依然超时。百度了一下,发现竟然有这样一个神奇的公式求一个数阶乘的因子个数:ei=[N/pi^1]+ [N/pi^2]+ …… + [N/pi^n] 其中[]为取整,显然,要对素数打表预处理,然后用唯一分解定理即可:M=(e1+1)(e2+1)……*(en+1)
代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=1e9+7;
int n,m;
ll k[83]={0};
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431};

int num(int n, int p)
{
    int ans = 0;
    int tmp = p;
    while(p <= n)
    {
        ans += n/p;
        p = p*tmp;
    }
    return ans;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ll ans=1;
        for(int i=0;i<=82;i++)
        {

            int a=num(n,prime[i]);
            int b=num(m,prime[i]);
            int c=num(n-m,prime[i]);
            ans*=(a-b-c+1); 
        }

        printf("%lld\n",ans);

    }
    return 0;
}

这里要注意组合数 C n m = n ! m ! ( n m ) ! 的因子数要对每一个素数单独累乘,如代码中for循环所示。
C - Longge’s problem POJ - 2480
题目大意:求1-n的gcd(i,n)的和,题目都说到这份了显然暴力求解不可取。
不过通过打表可以发现他们的最大公约数出现是有规律的
因为i = a * b
n = a * d且 gcd(b, d) = 1
所以gcd(i, n) = a gcd(1,n/i)=1 发现每个最大公约数出现的次数为phi(n/i),使用欧拉函数即可。
问题又来了,单个分开求会T,这里给出大神的代码(还没看懂为啥原来的会T)
代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define LL long long
#define MOD 1000000007
using namespace std;
int main(){
    LL n;
    while(scanf("%lld",&n)!=EOF){
        LL ans=1;
        for(LL i=2;i*i<=n;i++)
            if(n%i==0){
                LL k=0,p=1;
                while(n%i==0){
                    k++;
                    p*=i;
                    n/=i;
                }
                ans*=k*(p-p/i)+p;
            }
        if(n>1)
            ans*=2*n-1;
        printf("%lld\n",ans);
    }
    return 0;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        ll ans=0;

        for(int i=1;i<=n/2+1;i++)
        {

            if(n%i==0)
            {
                ans+=(i*phi(n/i));
            }
        }   
        ans+=n;
        printf("%lld\n",ans);
    }
}

E - Happy 2006 POJ - 2773
题目大意:按升序找出与n互质的第k个数。
因为gcd(a,b)=gcd(a+kb,b)=d,推断可得与b互质的每个数具有周期性
核心代码

for(int i=1;i<=n;i++)
            if(gcd(n,i)==1)
                prime[res++]=i;
        if(k%res)
            cout<<k/res*n+prime[k%res-1]<<endl; 
        else
            cout<<(k/res-1)*n+prime[res-1]<<endl;

H - X-factor Chains POJ - 3421
题目大意:就是给你一个数,要求你输出将这个数分解成因式相乘,并且后面一个因子至少是前面一个因子的2倍,问最长的因式相乘链有多长,有几条最长的因式相乘链。
其实就是一个排列组合题,由唯一分解定理得最长链长即为因子个数c,链条数即为k= c ! e 1 ! e 2 ! · · · e n ! .

I - Pseudoprime numbers
判断素数+快速幂暴力即可

未完待续······

此外,积累几个极致筛素数和求逆元的方法
埃筛(利用素数倍数一定是合数来优化)

//O(nlognlogn) 埃筛--------------埃拉托斯特尼筛法,或者叫埃氏筛法
bool prime[a];
void init(){
    for(int i=2;i<a;i++)
    prime[i]=true;

    for(int i=2;i*i<a;i++)//由for(int i=2;i<a;i++)改进 
    {
        if(prime[i])
        {
            for(int j=i*i;j<a;j+=i)//由for(int j=i*2;j<a;j+=i)改进 
                prime[j]=false;
        }
    }
}

神奇的线性筛

//O(n) 线性筛 
bool prime[maxn];//prime[i]表示i是不是质数 
int p[maxn], tot=0;//p[maxn]用来存质数 
void init(int a){
    for(int i = 2; i <= a; i ++) prime[i] = true;//初始化为质数 
    for(int i = 2; i <= a; i++){
        if(prime[i]) p[tot ++] = i;//把质数存起来 
        for(int j = 0; j < tot && i * p[j] <= a; j++){
            prime[i * p[j]] = false;
            if(i % p[j] == 0) break;//******保证每个合数被它最小的质因数筛去****** 
        }
    }    
}

杜教筛???洲阁筛???以后再学吧

逆元线性筛 ( P为质数 )
求1,2,…,N关于P的逆元(P为质数)
复杂度:O(N)

const int mod = 1000000009;
const int maxn = 10005;
int inv[maxn];
inv[1] = 1;
for(int i = 2; i < 10000; i++)
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;

没有互质限制
ans=a/b mod m=a mod(mb)/b;

阶乘逆元!

inv[maxn]=mod_pow(fac[maxn],mod-2);
for(ll i=maxn-1;i>=0;i--)
    inv[i]=(inv[i+1]*(i+1))%mod;

还有什么莫比乌斯反演···那些等会了再更吧~

数学太难了!!!

这周总结先到这里,终于能睡一个>=6小时的觉了,明天开始图论专题,Fighting!!!

猜你喜欢

转载自blog.csdn.net/qq_40538328/article/details/81437198
今日推荐