• Noip数学整理


    Noip数学整理

    • 因为某些原因, Noip对于数学方面的考纲仅停留在比较小的一部分,而这一部分在平常的做题中接触较少我做的题目太少, 为了防止NOIP爆炸, 整理一些Noip的数学知识还是有用的。

    1 取模相关

    • n%p所得结果的正负由n决定,与p无关。如:7%4=3-7%4=-3-7%-4=-3 ---xun学姐
    • 欧拉定理 (alpha^{phi(p)} equiv 1 mod(p))
    • 当p为质数的特殊情况 $alpha^{p - 1} equiv 1 $
    • Noip2014式取模, 如果f(x) = 0, 那么f(x) % p = 0, 在式子中带入数字看脸计算。
    • 常数较小的取模方式
    void upd(int &x, int y)
    {
        x += y, x -= x >= mod ? mod : x;
    }
    

    2 质数相关

    质数判定

    • 根号判断比较稳
    bool check(int x)
    {
        for(int i = 2; i * i <= x; i++)
            if(x % i == 0)return false;
        return true;
    }
    
    • Miller robin比较骚(-------)chentongfei
    bool check(int x)
    {
        for(int i = 1; i <= 20; i++)
        {
            int op = read() % x + 1;
            while(op == x) op = rand() % x + 1;
            if(poww(op, x - 1, x) != 1) return false;
        }
        return true;
    }
    
    • 线性筛比较经典
    
    void shai()
    {
        isnt[1] = true;
        for(int i = 2; i <= n; i++)
        {
            if(!isnt[i]) sta[++tp] = i;
            for(int j = 1; j <= tp && i * sta[j] <= n; j++)
            {
                isnt[i * sta[j]] = true;
                if(i % sta[j] == 0) break;
            }
        }
    }
    
    
    • 区间筛比较没用
    void shai(ll l, ll r)
    {
        for(int i = 2; i * i <= r; i++)
        {
            if(!isnt[i])
            {
                for(int j = 2 * i; j * j<= r; j += i) isnt[j] = true;
                for(ll j = max(2ll, (l + i - 1) / i ) * i; j <= r; j += i) vis[j - l] = true;
            }
        }
        for(ll i = 0; i <= b - a; i++) if(!vis[i]) sta[++tp] = i + a;
    }
    

    质数应用

    • 用来取模

    3.基本操作

    快速幂 or 慢速乘

    • ???
    • O1快速乘吼啊
    inline long long multi(long long x,long long y,long long mod) {
    	long long tmp=(x*y-(long long)((long double)x/mod*y+1.0e-8)*mod);
    	return tmp<0 ? tmp+mod : tmp;
    }
    
    

    逆元

    费马小定理
    • 一定要检查一下模数是不是质数啊啊啊啊
    exgcd求逆元

    (a * b equiv 1; mod; mRightarrow a * b - m * y = 1) 如果解出(gcd(a, m) = 1), 那么就存在逆元了

    int inv(int a, int p)
    {
        int x, y;
        int tmp = gcd(a, p, x, y);
        if(tmp != 1) return false;
        return (x + p) % p;
    }
    

    质因数分解

    根号算法
    • 枚举到根号大小就能找出所有质因子了
    pollard-Robin
    • 基于随机化, 鸽笼定理, floyd判环的算法
    • 写起来挺舒适的
    
    bool robin_miller(ll n) { //判断是否素数
    	if(n < 2)    return false;
    	if(n == 2)  return true;
    	if(!(n & 1))    return false;
    
    	ll u = n - 1, t = 0;
    	while(!(u & 1)) u >>= 1, t++;
    	if(t >= 1 && (u & 1) == 1) {
    		for(int i = 0; i < s; i++) {
    			ll a = rand() % (n-1) + 1;
    			if(witness(a, n, u, t)) return false;   //不是素数
    		}
    	}
    	return true;    //是素数
    }
    
    ll gcd(ll a, ll b) {
    	if(a == 0)  return 1;
    	if(a < 0)    return gcd(-a, b);
    	while(b) {
    		ll t = a % b;
    		a = b;
    		b = t;
    	}
    	return a;
    }
    
    ll pollard_rho(ll x, ll c) {
    	ll i = 1, x0 = rand() % x;
    	ll y = x0;
    	ll k = 2;
    	for(;;) {
    		i++;
    		x0 = (multi_mod(x0, x0, x) + c) % x;
    		ll d = gcd(y - x0, x);
    		if(d != 1 && d != x) return d; 
    		if(y == x0) return x;
    		if(i == k) {
    			y = x0;
    			k += k;
    		}
    	}
    }
    
    void find_fac(ll n) {
    	if(n == 1)    return;
    	if(robin_miller(n)) {
    		prime_factor.push_back(n);
    		return;
    	}
    	ll p = n;
    	while(p >= n) p = pollard_rho(p, rand() % (n-1) + 1);
    	find_fac(p);
    	find_fac(n / p);
    }
    
    

    4.方程相关

    不定方程

    • 扩展欧几里得求同余方程 (ax + by = gcd(a, b))
    int exgcd(int a, int b, int &x, int &y)
    {
        if(!b)
        {
            x = 1, y = 0;
            return a;
        }
        int ans = exgcd(b, a % b, x, y), tmp = x;
        x = y, y = tmp - a / b * y;
        return ans;
    }
    
    • 得到的解只是一组 (ax + by = g), 让(x += g / b, y -= g/a)依旧成立
    • 所以通解可以表示成(x = x_1 + frac{b}{gcd(a,b)}, y = y_1 + frac{a}{gcd(a, b)})
    • 对于求解(ax + by = c)的同余方程, 首先判断c是否是(gcd(a, b))的倍数, 不是的话则无解
    • 然后求出(ax + by = gcd(a, b)) 的一组解, 之后让xy都乘以 (g / gcd(a, b))即可

    线性同余方程组

    • 互质情况: 中国剩余定理
    • 求解

    [x equiv a_1; mod; m_1 ]

    [x equiv a_2; mod; m_2 ]

    [x equiv a_3; mod; m_3 ]

    [x equiv a_4; mod; m_4 ]

    得到的通解可以表示成

    [x = kM + sum_{i = 1}^{n}a_it_iM_i ]

    其中(k in Z; M = prod m_i; M_i = frac{M}{M_i}; t_i = M_i^{-1}; mod m_i)

    
    int cn(int n)
    {
        int sum = 1, ans = 0, x, y;
        for(int i = 1; i <= n; i++) sum *= m[i];
        for(int i = 1; i <= n; i++)
        {
            int op = sum / m[i];
            int zz = exgcd(op, m[i], x, y);
            ans = (ans + g[i] * op * x) % sum; 
        }
        return (sum + ans) % sum;
    }
    
    • 不互质情况, 扩展中国剩余定理
    • 感觉和中国剩余定理不是很相似, 主要是基于方程的合并
    • 首先考虑两个同余方程

    [x equiv a_1; mod; m_1\ x equiv a_2; mod; m_2 ]

    • 化成另一个形式

    [x = n_1 * m_1 + a_1\ x = n_2 * m_2 + a_2 ]

    • 联立可得

    [n_1 * m_1 + a_1 = n_2 * m_2 + a_2\ n_1 * m_1 - n_2 * m_2 = a_2 - a_1 ]

    • 有解的前提是

    [gcd(m_1, m_2) |(a_2 - a_1) ]

    [d = gcd(m_1, m_2)\ c = a_2 - a_1 ]

    [n_1 frac{m_1}{d} - n_2 frac{m_2}{d} = frac{c}{d}\ n_1 frac{m_1}{d} equiv frac{c}{d} mod frac{m_2}{d} ]

    • 移项

    [n_1 equiv frac{c}{d} * inv(frac{m_1}{d}, frac{m_2}{d}) mod frac{m_2}{d}\ n_1 = frac{c}{d} * inv(frac{m_1}{d}, frac{m_2}{d}) + y_1 * frac{m_2}{d} ]

    然后(n_1)代入最上面的狮子可以得到

    [x = (frac{c}{d} * inv(frac{m_1}{d}, frac{m_2}{d}) + y_1 * frac{m_2}{d}) * m_1 + a_1\ x = m_1 * frac{c}{d} * inv(frac{m_1}{d}, frac{m_2}{d}) + y_1 * frac{m_2 m_1}{d} + a_1\ x equiv m_1 * frac{c}{d} * inv(frac{m_1}{d}, frac{m_2}{d}) + a_1 mod frac{m_2 m_1}{d} ]

    • 然后就是个新方程了
    • 当然也适用于互质情况
    • 板子
    int ex_crt()
    {
        int a1 = a[1], m1 = m[1], a2, m2;
        for(int i = 2; i <= n; i++)
        {
            a2 = a[i], m2 = m[i];
            int c = a2 - a1;
            int d = gcd(m2, m1);
            if(c%d) return -1;
            ll k = inv(m1 / d, m2 / d);
            a1 = a1 * c / d * k + a1;
            m1 = m1 * m2 / d;
        }
        return a1;
    }
    
    

    裴蜀定理

    • 对于方程 (ax + by = c) 有解当且仅当 (gcd(a,b) | c) 可以扩展到多元, 经常被出题人用来出套路题

    5.数列相关

    卡特兰数

    • 前几项!!!! (1 1 2 5 14 42 132)
    递推式

    [C_n = sum_{i = 1}^{n - 1} C_i * C_{n - i} \ C_n = C_{n - 1} * (4n - 2)/ (n + 1) ]

    组合式

    [h(n) = C_{2n}^{n} - C_{2n}^{n - 1}\ h(n) = frac{C_{2n}^{n}} { (n + 1)} ]

    意义
    • 括号化问题。矩阵链乘: P=A1×A2×A3×……×An,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?
    • 将多边行划分为三角形问题。将一个凸多边形区域分成三角形区域(划分线不交叉)的方法数
    • 出栈次序问题。一个栈(无穷大)的进栈序列为1、2、3、...、n,有多少个不同的出栈序列?
    • 给顶节点组成二叉树的问题。

    组合数

    求法
    杨辉三角
    • 可更改性比较好, 适用于非质数取模
    阶乘法
    • 预处理On, 计算一次logn, 需要On的空间
    卢卡斯定理
    • 适用于模数较小且为质数

    [Lucas(n, m, p) = C_{n \% p}^{m \% p} * Lucas(n/p, m/p, p) ]

    扩展卢卡斯
    • 适用于模数较小非质数或者模数较大但是分解后因子较小
    • 相当于我们对于模数分解后分别求出在mod某个数字下组合数是多少
    • 然后使用crt合并就好了
    • 当质因数分解不干净的时候, 我们要求(Lucas(n, m, p^t))这个也是个问题
    • 这里利用循环节来统计阶乘中不含p因子的数字乘积, 对于含p因子的提出来递归计算
    • Noip考这个我直播吃拖鞋
    • 贴个板子
    #include <bits/stdc++.h>
    using namespace std;
    
    int t,opt;
    long long p[50],a[50];//存储质因数p的t次方,a存储CRT要用的余数
    map<long long,long long> mp;
    long long ji;
    inline long long ksm(long long x,long long y,long long mod)
    {
        long long res=1;
        while(y)
        {
            if(y&1)
            res=(res*x)%mod;
            x=(x*x)%mod;
            y>>=1;
        }
        return res;
    }
    inline void exgcd(long long a,long long b,long long &x,long long &y)
    {
        if(!b)
        {
            x=1;
            y=0;
        }
        else
        {
            exgcd(b,a%b,y,x);
            y-=a/b*x;
        }
    }
    inline long long inv(long long a,long long b)
    {
        long long x=0,y=0;
        exgcd(a,b,x,y);
        x=(x%b+b)%b;
        if(!x)
        x+=b;
        return x;
    }
    inline long long cal(long long n,long long x,long long mod)//递归计算除了x的若干次方之外的部分(第一、第三部分) 
    {
        if(!n)
        return 1;
        long long ans=1;//提出来的那些不含因子x的乘积 
        if(n/mod)//有整块的 
        {
            for(int i=1;i<=mod;++i)
            {
                if(i%x)//不含因子x
                ans=ans*i%mod;
            }
            ans=ksm(ans,n/mod,mod);//有循环节,所以乘积用快速幂计算即可 
        }
        for(int i=n/mod*mod+1;i<=n;++i)
        {
            if(i%x)
            ans=ans*i%mod;
        }
        return ans*cal(n/x,x,mod)%mod;//当前的不含因子x的乘积乘以递归下去求的剩余阶乘部分的结果  
    }
    //计算出对于每一个质数的若干次方取模后的结果 
    inline long long exlucas(long long m,long long n,long long x,long long mod)//x是当前质数 
    {
        if(n>m)
        return 0;
        int cnt=0;
        for(int i=m;i;i/=x)
        cnt+=i/x;
        for(int i=n;i;i/=x)
        cnt-=i/x;
        for(int i=m-n;i;i/=x)
        cnt-=i/x;
        long long ans=ksm(x,cnt,mod)*cal(m,x,mod)%mod*inv(cal(n,x,mod),mod)%mod*inv(cal(m-n,x,mod),mod)%mod;
        return ans*(ji/mod)%ji*inv(ji/mod,mod)%ji;//CRT合并! 
    }
    inline long long gcd(long long x,long long y)
    {
        return y?gcd(y,x%y):x;
    }
    
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&opt);
            if(opt==1)
            {
                long long x,y,p;
                scanf("%lld%lld%lld",&x,&y,&p);
                printf("%lld
    ",ksm(x,y,p));
            }
            else if(opt==2)
            {
                mp.clear();
                long long aa,b,p;
                int pd=0;
                scanf("%lld%lld%lld",&aa,&b,&p);
                if(b==1)
                {
                    printf("0
    ");
                    pd=1;
                }
                if(pd==1)
                continue;
                long long d=gcd(aa,p),t=1,k=0;
                while(d!=1)
                {
                    if(b%d)
                    {
                        printf("Math Error
    ");
                        pd=1;
                        break;
                    }
                    ++k;
                    b/=d;
                    p/=d;
                    t=(t*(aa/d))%p;
                    if(b==t)
                    {
                        printf("%lld
    ",k);
                        pd=1;
                        break;
                    }
                    d=gcd(aa,p);                
                }
                if(pd==1)
                continue;
                long long m=ceil(sqrt(p)),ans;
                for(int j=0;j<=m;++j)
                {
                    if(j==0)
                    {
                        ans=b%p;
                        mp[ans]=j;
                        continue;
                    }
                    ans=(ans*aa)%p;
                    mp[ans]=j;
                }
                long long x=ksm(aa,m,p);
                ans=t;
                for(int i=1;i<=m;++i)
                {
                    ans=(ans*x)%p;
                    if(mp[ans])
                    {
                        x=i*m-mp[ans];
                        printf("%lld
    ",x+k);
                        pd=1;
                        break;
                    }
                }
                if(!pd)
                printf("Math Error
    ");
            }
            else 
            {
                long long x,y,mod;
                scanf("%lld%lld%lld",&x,&y,&mod);
                ji=mod;//mod就是CRT的那个因子乘积和! 
                if(mod==1)
                {
                    printf("0
    ");
                    continue;
                }
                memset(p,0,sizeof(p));
                memset(a,0,sizeof(a));
                int cnt=0;//记录质数个数 
                for(int i=2;i*i<=mod;++i)
                {
                    if(mod%i==0)
                    {
                        p[++cnt]=1;
                        while(mod%i==0)
                        {
                            p[cnt]*=i;
                            mod/=i;//把当前因子除掉对看是否是后面数的倍数无影响 
                        }
                        a[cnt]=exlucas(y,x,i,p[cnt]);                   
                    }
                } 
                if(mod>1)
                {
                    p[++cnt]=mod;
                    a[cnt]=exlucas(y,x,mod,mod);
                }
                long long res=0;
                for(int i=1;i<=cnt;++i)
                res=(res+a[i])%ji;
                printf("%lld
    ",res);
            }
        }
        return 0;
    }
    
    常见模型和性质
    奇偶性
    • 当且仅当n & m == m(C_n^m) 为奇数
    插板法
    • n种元素,每种可选择任意次或者不选,共选m

    [C_{n +m - 1} ^{n - 1} ]

    全错排公式

    [f[i] = (i - 1)(f[i - 1] + f[i - 2]) ]

    • 情况1:插入第i个元素时,前i-1个已经错位排好,则选择其中任意一个与第i个互换一定满足要求,选择方法共i-1种,前i-1位错排f[i-1]种,记f[i-1]*(i-1)

    • 情况2:插入第i个元素时,前i-1个中恰有一个元素a[j]使得a[j]=j,其他i-2个错位排好,则将i与j交换,j在i-2位中的插入共i-1种,前i-2位错排f[i-2]种,记f[i-2]*(i-1)

    6.函数相关

    • Noip函数???
    • 貌似只有一次函数合二次函数了吧QAQ
  • 相关阅读:
    iOS下JS与OC互相调用(三)--MessageHandler
    在xcode6中使用矢量图(iPhone6置配UI)
    UITextField增加textDidChange回调功能
    IOS开发之格式化日期时间(转)
    ios中将事件同步到系统日历
    xcode中一些便捷用法@literals简写
    JavaScript
    fuzz for test of the Net::HTTP::GET
    perl6 中将 字符串 转成十六进制
    Net::HTTP 一次添加 cookie, body 发送post请求
  • 原文地址:https://www.cnblogs.com/luoyibujue/p/10505348.html
Copyright © 2020-2023  润新知