zoukankan      html  css  js  c++  java
  • 二次剩余学习笔记

    定义:对一个数n,如果不是p的倍数且模p同余于某个数的平方,则称n为模p的二次剩余,即对一个数n,求解方程$x^2equiv n(mod p)$

    二次剩余的作用:对于一个数n,如果要求$sqrt{n} mod p$,可以看为n是否是模p的二次剩余,如果方程$x^2equiv n(mod p)$有解,就会有$xequiv sqrt{n} (mod p)$,就可以用x代替$sqrt{n}$

    下面仅考虑p为奇素数的情况

    定理1:对于$x^2equiv n(mod p)$,能满足“n是模p的二次剩余”的n一共有$frac{p-1}{2}$个(不包括0),非二次剩余有$frac{p-1}{2}$个

    证明:设$u^2=a*p+n$,即$u^2equiv n(mod p)$

    $ecause forall tin mathbb{Z},(u+t*p)^2=u^2+2*u*t*p+(t*p)^2=(a+t^2 * p + 2*u*t)*p+n$

    $ herefore u^2equiv (u+t*p)^2 (mod p)$

    所以我们只用考虑当$xin [0,p-1]$,满足“n是模p的二次剩余”的n一共有多少个

    假设存在两个不同的数u,v带入方程,使得$u^2equiv n(mod p)$,$v^2equiv n(mod p)$

    那么就有$u^2equiv v^2(mod p)$,显然$(u^2 - v^2)mid p$,即$(u-v)*(u+v)mid p$

    因为$u,vin [0,p-1]$,所以$(u-v) mid p$,只可能u+v=p,那么这样的u,v有$frac{p-1}{2}$对,即满足“n是模p的二次剩余”的n有$frac{p-1}{2}$个


    我们引入勒让德符号$(frac{n}{p})$

    当$p mid n$且n是p的二次剩余时,$(frac{n}{p})=1$

    当$p mid n$且n不是p的二次剩余时,$(frac{n}{p})=-1$

    当$pmid n$时,$(frac{n}{p})=0$

    欧拉判别准则:$(frac{n}{p})equiv n^{frac{p-1}{2}}(mod p)$,n是p的二次剩余,当且仅当$n^{frac{p-1}{2}}equiv 1(mod p)$,n不是p的二次剩余,当且仅当$n^{frac{p-1}{2}}equiv -1(mod p)$

    证明:由费马小定理得$x^{p-1}equiv 1(mod p)$一定存在解

    由于$x^2equiv n(mod p)$

    所以$x^{p-1}equiv (x^2)^{frac{p-1}{2}}equiv n^{frac{p-1}{2}}(mod p)$

    所以n为p的二次剩余的条件为$n^{frac{p-1}{2}}equiv 1(mod p)$


    二次剩余解(x)的个数:假设存在多组解$x_0,x_1$使得$x^2equiv n(mod p)$成立,于是有$x_{0}^2equiv x_{1}^2$,移项后得$(x_0-x_1)*(x_0+x_1)equiv 0(mod p)$

    因为$x_0,x_1in [0,p-1]$

    当$x_0=x_1$时,$(x_0-x_1)*(x_0+x_1)equiv 0(mod p)$成立

    当$x_0 eq x_1$时,$(x_0+x_1)equiv 0(mod p)$成立,此时只存在两个不相等的解

    Cipolla定理:设a满足$omega =a^2-n$且$omega $是模p得非二次剩余,即$x^2equiv omega (mod p)$无解,那么$xequiv (a+sqrt{omega })^{frac{p+1}{2}}$是$x^2equiv n(mod p)$的解

    所以我们可以随机一个a,然后来找到一个满足条件的解

    建立一个类似于“复数域”的域,定义“虚数单位”为$omega$,然后进行求解,由于满足“n是模p的二次剩余”的n一共有$frac{p-1}{2}$个,所以随机次数的期望为2


    例题:

    luogu 5491 [模板]二次剩余

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    
    using namespace std;
    
    typedef long long ll;
    
    int T;
    ll n, mod, I;
    
    struct complex {
        ll r, i;
        complex (ll tr = 0, ll ti = 0) : r(tr), i(ti) { }
    };
    
    inline bool operator == (complex x, complex y) {
        return x.r == y.r && x.i == y.i;
    }
    
    inline complex operator * (complex x, complex y) {
        ll a = (x.r * y.r + I * x.i % mod * y.i) % mod;
        ll b = (x.i * y.r + x.r * y.i) % mod;
        return complex(a, b);
    }
    
    complex power(complex a, ll n)
    {
        complex res = 1;
        while (n) {
            if (n & 1) res = res * a;
            a = a * a;
            n >>= 1;
        }
        return res;
    }
    
    bool check(ll x)
    {
        return power(x, (mod - 1) >> 1) == 1;
    }
    
    void solve(ll n, ll &x, ll &xx)
    {
        ll a = rand() % mod;
        while (!a || check((a * a + mod - n) % mod)) a = rand() % mod;
        I = (a * a - n + mod) % mod;
        x = power(complex(a, 1), (mod + 1) >> 1).r;
        xx = mod - x;
    }
    
    ll pint(ll a, ll n)
    {
        ll res = 1;
        while (n) {
            if (n & 1) res = res * a % mod;
            a = a * a % mod;
            n >>= 1;
        }
        return res;
    }
    
    int main()
    {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        scanf("%d", &T);
        while (T--) {
            scanf("%d%lld", &n, &mod);
            if (0 == n) {
                printf("0
    ");
                continue;
            }
            if (1 != pint(n, (mod - 1) / 2)) {
                printf("Hola!
    ");
                continue;
            }
            ll x = 0, xx = 0;
            solve(n, x, xx);
            if (x == xx) {
                printf("%lld
    ", x);
                continue;
            }
            if (x > xx) swap(x, xx);
            printf("%lld %lld
    ", x, xx);
        }
        return 0;
    }
    luogu 5491

    zoj 3774 Power of Fibonacci

    题意:求$sum_{i=1}^{n}F_{i}^k$,$F_{i}$为斐波那契数列的第i项,模1000000009

    思路:斐波那契数列的通项公式为$F_n=frac{1}{sqrt{5}}[(frac{1+sqrt{5}}{2})^n-(frac{1-sqrt{5}}{2})^n]$

    令$a=frac{1+sqrt{5}}{2}$,$b=frac{1-sqrt{5}}{2}$,则$F_n=frac{1}{sqrt{5}}(a^n-b^n)$

    $F_n^k=(frac{1}{sqrt{5}})^k (a^n-b^n)^k$

    $(a^n-b^n)^k$二项式展开得$(a^n-b^n)^k=C_k^0(a^n)^k+C_k^1(a^n)^{k-1}(-b^n)^1+dots +C_k^k(-b^n)^k=sum_{r=0}^{k}(-1)^rC_k^r(a^{k-r}*b^r)^n$

    将前n项相加得$res=(frac{1}{sqrt{5}})^k sum_{r=0}^ksum_{i=1}^{n}(-1)^rC_k^r(a^{k-r}*b^r)^i=(frac{1}{sqrt{5}})^k sum_{r=0}^k(-1)^rC_k^rsum_{i=1}^{n}(a^{k-r}*b^r)^i$

    令$t=a^{k-r}*b^r$,$sum_{i=1}^{n}(a^{k-r}*b^r)^i=sum_{i=1}^{n}t^i$,为等比数列,求和后为$frac{t*(t^n-1)}{t}$,预处理出$a^i$和$b^i$即可O(1)求出t

    所以$res=(frac{1}{sqrt{5}})^ksum_{i=0}^{k}(-1)^rC_k^rfrac{t*(t^n-1)}{t-1}$

    根据二次剩余,$sqrt{5}equiv 383008016 mod 1000000009$,$frac{1+sqrt{5}}{2}equiv 691504013 mod 1000000009$,$frac{1-sqrt{5}}{2}equiv 308495997 mod 1000000009$

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    const ll mod = 1000000009;
    
    int T, k;
    ll n, A[N], B[N], fac[N], inv[N];
    
    void init()
    {
        A[0] = B[0] = fac[0] = 1;
        for (int i = 1; i < N; i++) {
            A[i] = A[i - 1] * 691504013 % mod;
            B[i] = B[i - 1] * 308495997 % mod;
        }
        for (int i = 1; i < N; i++) fac[i] = fac[i - 1] * i % mod;
        inv[1] = 1;
        for (int i = 2; i < N; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        inv[0] = 1;
        for (int i = 1; i < N; i++) inv[i] = inv[i] * inv[i - 1] % mod;
    }
    
    ll C(int n, int m)
    {
        return fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    
    ll power(ll a, ll n)
    {
        ll res = 1;
        while (n) {
            if (n & 1) res = res * a % mod;
            a = a * a % mod;
            n >>= 1;
        }
        return res;
    }
    
    int main()
    {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        init();
        scanf("%d", &T);
        while (T--) {
            scanf("%lld%d", &n, &k);
            ll res = 0;
            for (int r = 0; r <= k; r++) {
                ll t = A[k - r] * B[r] % mod, tr = n % mod;
                if (1 != t) {
                    tr = t * (power(t, n) - 1) % mod;
                    tr = tr * power(t - 1, mod - 2) % mod;
                }
                tr = tr * C(k, r) % mod;
                if (r & 1) res -= tr;
                else res += tr;
                res = (res % mod + mod) % mod;
            }
            ll invt = power(383008016, mod - 2);
            res = res * power(invt, k) % mod;
            printf("%lld
    ", res);
        }
        return 0;
    }
    Power of Fibonacci

    2020 Multi-University Training Contest 1 - 1005. Fibonacci Sum

    题意:求$(F_{0})^k+(F_{c})^k+(F_{2*c})^k+dots +(F_{n*c})^k$,模1000000009

    思路:和上一题差不多,时间卡的很紧,需要用到欧拉降幂

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    
    using namespace std;
    
    typedef long long ll;
    
    const ll mod = 1000000009;
    const int N = 100010;
    
    ll fac[N], inv[N], pa[N], pb[N], n, c, k;
    int T;
    
    inline void init()
    {
        fac[0] = 1;
        for (int i = 1; i < N; i++) fac[i] = fac[i - 1] * i % mod;
        inv[1] = 1;
        for (int i = 2; i < N; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
        inv[0] = 1;
        for (int i = 1; i < N; i++) inv[i] = inv[i] * inv[i - 1] % mod;
    }
    
    inline ll C(int n, int m)
    {
        return fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    
    inline ll power(ll a, ll n)
    {
        ll res = 1;
        while (n) {
            if (n & 1) res = res * a % mod;
            a = a * a % mod;
            n >>= 1;
        }
        return res;
    }
    
    int main()
    {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        init();
        scanf("%d", &T);
        while (T--) {
            scanf("%lld%lld%lld", &n, &c, &k);
            ll res = 0;
            pa[0] = pb[0] = 1;
            pa[1] = power(691504013, c % (mod - 1));
            pb[1] = power(308495997, c % (mod - 1));
            for (int i = 2; i <= k; i++) {
                pa[i] = pa[i - 1] * pa[1] % mod;
                pb[i] = pb[i - 1] * pb[1] % mod;
            }
            for (int r = 0; r <= k; r++) {
                ll t = pa[k - r] * pb[r] % mod;
                ll tn = power(t, n % (mod - 1));
                ll tr = t * (tn - 1) % mod * power(t - 1, mod - 2) % mod;
                if (1 == t) tr = n % mod;
                if (r & 1) {
                    res += mod - C(k, r) * tr % mod;
                    res %= mod;
                }
                else {
                    res += C(k, r) * tr % mod;
                    res %= mod;
                }
            }
            ll invt = power(383008016, mod - 2);
            res = res * power(invt, k) % mod;
            printf("%lld
    ", res);
        }
        return 0;
    }
    Fibonacci Sum
  • 相关阅读:
    实习小白::(转) Cocos2d-x 3.0开发(五)关联程序逻辑与cocoStudio导出文件
    实习小白::(转)Cocos2d-x 3.0开发(六)使用cocoStudio创建一个骨骼动画
    实习小白::(转) Cocos2d-x 3.0 开发(七)在程序中处理cocoStudio导出动画
    实习小白::(转) cocos2d-x使用cocosStudio编辑的动画文件
    (转)cocos2d-x 每帧动画的播放设置一个监听函数的做法
    Filter
    使用Cookie记住用户名和密码
    动态规划
    热分布
    背包问题
  • 原文地址:https://www.cnblogs.com/zzzzzzy/p/13396537.html
Copyright © 2011-2022 走看看