2016暑期集训2——数论(知识点,模板,简单题题解)

15 篇文章 0 订阅
10 篇文章 0 订阅

2016暑期集训2——数论(知识点,模板,简单题题解)

知识点

  1. 模运算。除法取模可以利用逆元。比如a对模m的逆是b,那么(k/a)modm=(k mod m)*(b mod m) mod m。以及快速幂。

  2. 区间素数筛。要筛a到b,先打一个1到根号b的表,在往上筛。

  3. 扩展欧几里得。扩展欧几里德算法是用来在已知a, b时求解一组x,y,使它们满足:ax+by = gcd(a, b) ,且abs(x)+abs(y)最小。可以利用它求逆。

  4. 容斥原理!!!很不好想!!!HDU 4135 和下面一道题CodeForces 630K
    比如求1到某范围内与2.3.5互质的数个数,可以先求不与他们互质的数,就是求2的约数个数+3的约数个数+5的约数个数-6的约数个数-10的约数个数-15的约数个数+30的约数个数。

  5. 质因数分解。根号n的复杂度

  6. 欧拉函数。对正整数n,欧拉函数是少于或等于n的数中与n互质的数的数目。例如phi(8)=4,因为1,3,5,7均和8互质。Euler函数表达通式:phi(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn),其中p1,p2……pn为x的所有素因数,x是不为0的整数。phi(1)=1(唯一和1互质的数就是1本身)。可以打表,思路类似素数筛。

  7. 组合数计算,取模。

    1. 当n,m很小的时候(不需要取模即可表示),只需计算正常计算即可。只需注意计算时要防止乘爆,应采取先乘后除的方法,先乘(n-i),后除(1+i)。而且C(a,b)=C(a,a-b),可以让b=min(b,a-b)。
    2. 若n或m固定,则可以用组合数性质提前打表处理。
    3. 若组合数较大需要取模,则应采用计算公式C(n,m) = n!/m!/(n-m)!其中除法注意要用逆代替,其中逆可以先预处理,打好表,也可以先求(n-m)!%MOD,在取逆。
    4. Lucas定理。当a,b比较大,容易超时,这时可以用Lucas定理。
  8. 快速幂求逆。利用费马小定理,a^(n-2)= 1(mod n)

模板

  • 快速幂
int q_pow ( int a , int b )
{
    int res = 1;
    while ( b > 0 )
    {
        if ( b % 2 )
        {
            res = res*a;
        }
        a = a*a;
        b = b / 2;
    }
    return res;
}
  • 组合数
ll C ( ll a , ll b )
{
    ll res = 1;
    for ( ll i = 1; i <= b; i++ )
    {
        res *= ( a - i + 1 );
        res /= ( i );
    }
    return res;
  • 组合数取模
ll extend_gcd ( ll a , ll b , ll &x , ll &y )
{
    if ( b == 0 )
    {
        x = 1;
        y = 0;
        return a;
    }
    else
    {
        ll r = extend_gcd ( b , a%b , y , x );
        y -= x*( a / b );
        return r;
    }
}
ll mod_inverse ( ll a , ll m )
{
    ll x , y;
    ll d = extend_gcd ( a , m , x , y );
    if ( d == 1 )
    {
        return ( x%m + m ) % m;
    }
    return -1;
}
ll CMOD ( ll a , ll b ,ll c)
{
    //b = min ( b , a - b );
    ll res = 1;
    ll ans = 1;
    for ( ll i = 1; i <= b; i++ )
    {
        res = res*( a - i + 1 ) % c;
        ans = ans*i%c;
    }
    return res*mod_inverse ( ans , c ) % c;
}
  • 素数筛
    for (int i = 2; i < MAXN; i++)
    {
        if (isprime[i]==0)
        {
            for (j = 2; i*j < MAXN; j++)
            {
                isprime[i*j] = 1;
            }
            vec.push_back(i);
        }
    }
  • 欧几里得,拓展欧几里得,利用拓展欧几里得求逆。
 int gcd(int a,int b)
{
    return b==0 ? a : gcd(b,a%b);
}
int extend_gcd(int a,int b,int &x,int &y)
{
    if(b==0) 
    {
        x = 1;
        y = 0;
        return a;
    } 
    else 
    {
        int r = extend_gcd(b,a%b,y,x);
        y -= x*(a/b);
        return r;
    }
}
int mod_inverse(int a,int m)
{
    int x,y;
    int d = extend_gcd(a,m,x,y);
    return (x%m+m)%m;
}
  • 欧拉函数表。
//phi(x)=x*(1-1/p1)(1-1/p2)(1-1/p3)…(1-1/pn),其中p1,p2……pn为x的所有素因数
//大体思路就是每次用一个质数往上筛,把因子包含这个质数的数乘上(1-1/p),p是该质数
for(i=1;i<MAXN;i++)
{
    phi[i] = i;//先赋为x
}
for(i=2;i<MAXN;i++)
{
    if(phi[i]==i)//说明它是一个素数,因为前面处理时没处理到i,说明i不是某个数的倍数。
    {
        for(j=i;j<MAXN;j+=i)
        {
            phi[j] = phi[j]/i*(i-1);//乘
        }
    }
}
  • 打阶乘、阶乘的逆取模的表,组合数取模
LL fact[MAXN];
LL inv[MAXN];
LL f[MAXN];
void pre()
{
    //MOD是要模的数
    //fact是阶乘取模表,inv是阶乘的逆取模
    M(fact, 0);M(inv, 0);M(f, 0);
    const LL MOD=mod;
    fact[0]=1;
    f[0]=1;
    inv[0]=1;
    fact[1]=1;
    f[1]=1;
    inv[1]=1;
    for(int i=2;i<MAXN;i++)
    {
        fact[i]=(LL)(fact[i-1]*i)%MOD;
        f[i]=(LL)(MOD-MOD/i)*f[MOD%i]%MOD;
        inv[i]=(LL)inv[i-1]*f[i]%MOD;
    }
}

LL CMOD(LL a, LL b)
{
    if(b>a) return 0;
    if(b==a) return 1;
    LL res=0;
    res=(((fact[a]*inv[b])%mod)*inv[a-b])%mod;
    return res;
}
  • Lucas定理。
long long Lucas(long long a,long long b)
{
    if(b==0) return 1;
    return C(a%MOD,b%MOD)*Lucas(a/MOD,b/MOD)%MOD;
}

简单题题解

  • C -POJ 2407 裸的欧拉函数

    HustOJ
    POJ

  • E -CodeForces 630F 小组合数
    从n个人里面选5或6或7个。
    裸的小组合数,直接输出C(n,5)+C(n,6)+C(n,7)

    HustOJ
    CodeForces

  • D -CodeForces 630B 裸的快速幂
    求1.000000011的n次幂。

    HustOJ
    CodeForces

  • B -CodeForces 577C Vasya and Petya’s Game(不是裸的。)

    HustOJ
    CodeForces

    大意是A想一个1到n的数x,B知道n的情况下向A询问,x可以被y1整除么?可以被y2整除么…… B一气问完,A一起回答。问B最少问几次,每次的y是什么。
    做法是唯一分解定理,把n分解,每次问一个质因子,如果质因子的幂次也在n之内,那么该幂次也要问。比如n是26,你要光问5是区分不出5和25的。

    using namespace std;
    const int MAXN = 1007;
    typedef long double ld;
    typedef long long ll;
    
    int q_pow ( int a , int b )
    {
        int res = 1;
        while ( b > 0 )
        {
            if ( b % 2 )
            {
                res = res*a;
            }
            a = a*a;
            b = b / 2;
        }
        return res;
    }
    bool isprime [ MAXN ];
    vector<int> prime_list;
    int main ( )
    {
        int n;
        scanf ( "%d" , &n );
        if ( n == 1 ) printf ( "0\n" );
        else if ( n == 2 ) printf ( "1\n2\n" );
        //else if ( n == 3 ) printf ( "2\n2 3\n" );
        else
        {
            prime_list.clear ( );
            memset ( isprime , 0 , sizeof ( isprime ) );
            for ( int i = 2; i <= n; i++ )
            {
                if ( isprime [ i ] == 0 )
                {
                    for ( int j = 2; i*j <= n; j++ )
                    {
                        isprime [ i*j ] = 1;
                    }
                    prime_list.push_back ( i );
                }
            }
            int k = 0;
            queue<int> res;
            for ( int i = 0; i < prime_list.size ( ); i++ )
            {
                for ( int j = 1; ; j++ )
                {
                    int t = q_pow ( prime_list [ i ] , j );
                    if ( t <= n )
                    {
                        res.push ( t );
                    }
                    else break;
                }
            }
            printf ( "%d\n" , res.size ( ) );
            for ( int i = 0; !res.empty ( ); i++ )
            {
                if ( i == 0 )
                {
                    int temp = res.front ( );
                    printf ( "%d" , temp );
                    res.pop ( );
                }
                else
                {
                    int temp = res.front ( );
                    printf ( " %d" , temp );
                    res.pop ( );
                }
            }
            printf ( "\n" );
        }
        return 0;
    }
  • G -CodeForces 630K 容斥

    HustOJ
    CodeForces

    见上面容斥的讲解。

    int main ( )
    {
    ll n;
    scanf ( "%lld" , &n );
    int a [ 4 ] [ 10 ] = { { 2 , 3 , 5 , 7 , 0 , 0 , 0 , 0 , 0 , 0 , } , { 6 , 10 , 14 , 15 , 21 , 35 , 0 , 0 , 0 , 0 } , { 30 , 42 , 70 , 105 , 0 , 0 , 0 , 0 , 0 , 0 } , { 210 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } };
    ll res = n;
    for ( int i = 0; i < 4; i++ )
    {
        for ( int j = 0; j < 10; j++ )
        {
            if ( a [ i ] [ j ] != 0 )
            {
                if ( i % 2 == 0 )
                {
                    res -= ( n / a [ i ] [ j ] );
                }
                else
                {
                    res += ( n / a [ i ] [ j ] );
                }
            }
        }
    }
    printf ( "%lld\n" , res );
    return 0;
  • F -CodeForces 630G

    HustOJ
    CodeForces

    大意:5个A旗,3个B旗,放到n个桌子上。有多少种放法。直接组合。

    
    #include<cstdio>
    
    using namespace std;
    typedef long long ll;
    ll C ( ll a , ll b )
    {
        b = a - b;
        ll res = 1;
        for ( ll i = 1; i <= b; i++ )
        {
            res *= ( a - i + 1 );
            res /= ( i );
        }
        return res;
    }
    int main ( )
    {
        ll n;
        scanf ( "%lld" , &n );
        ll res=0;
        res = C ( 4 + n , n - 1 )*C ( 2 + n , n - 1 );
        printf ( "%lld" , res );
        return 0;
    }
  • H -数据有问题而且水题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值