zoukankan      html  css  js  c++  java
  • zhugezy的完美数学教室(v2.0)

    你找到了这个菜鸡的板子。。。
    2019/09/20:马上要ccpc了,更新一下,过十几个小时应该还会加一点东西(?)

    zhugezy的完美数学教室

    一些结论

    \(2C_n^2 + n = n^2,C_n^m=C_{n - 1}^m+C_{n-1}^{m-1}\)

    \(\sum_{i = 1}^niC_n^i=n2^{n-1},\sum_{i=1}^ni^2C_n^i=n(n+1)2^{n-2}\)

    \(C_n^{x+1}+2\sum_{i=0}^xC_n^i=\sum_{i=0}^{x+1}C_{n+1}^i\)

    \(\sum_{i=1}^n(-1)^{i-1}\frac{C_n^i}{i}=\sum_{i=1}^n\frac{1}{i}\)

    \(\sum_{i=0}^n(C_n^i)^2=C_{2n}^n\)

    1~n与n互质的数的和:\(\frac{n*\phi(n)+[n==1]}{2}\)

    \(\sum_{i=1}^ngcd(i,n)=\sum_{d|n}d\phi(n/d)\)

    1p模p的所有逆元值对应1p中所有的数,例如p=7时,1~6分别对应1,4,5,2,3,6

    $1 \leq i \leq n, \lfloor\frac{n}{i}\rfloor $最多只有\(2\sqrt{n}\)种取值

    \(\lfloor {\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor\)\(\lfloor\frac{n}{i}\rfloor\)取值相同的一段区间的右端点

    \(\lfloor\frac{n}{ab}\rfloor=\lfloor\frac{\lfloor\frac{n}{a}\rfloor}{b}\rfloor=\lfloor\frac{\lfloor\frac{n}{b}\rfloor}{a}\rfloor\)(对\(\lceil\rceil\)也成立)​

    n的所有因子之和(对n的质因数分解式)\(\sigma(n) = \frac{p^{e_1 + 1}_1 - 1}{p_1-1} *\frac{p^{e_2 + 1}_2 - 1}{p_2-1}*...*\frac{p^{e_k + 1}_k - 1}{p_k-1}\)

    n个数的全排列的逆序对数和:\(n!\frac{n(n-1)}{4}\)

    \(Cayley\)公式:\(n\)个带编号的结点组成\(s\)棵树的森林,其中指定\(s\)个不同的结点属于不同的树,这样的组成方案一共有\(F(n,s)=sn^{n-s-1}\)种。

    特别地,\(n\)个结点带编号的完全图一共有\(n^{n-2}\)个生成树,即\(n\)个结点带编号的无根树有\(n^{n-2}\)个。

    威尔逊定理:\(p\)为质数\(\ \ iff \ \ p|(p-1)!+1\)

    \(gcd(a^n−1,a^m−1)=a^{gcd(n,m)}−1\)

    \(a>b,gcd(a,b)=1\)时有\(gcd(a^m-b^m,a^n-b^n)=a^{gcd(m,n)}-b^{gcd(m,n)}\)

    \(G(n)=gcd(C_n^1,C_n^2,...,C_n^{n-1}),\)则对任意质数\(p\)和自然数\(i\),有\(G(p^i)=p\),否则\(G(n)=1\).

    \((n+1)lcm(C_n^0,C_n^1,...,C_n^n)=lcm(1,2,...,n+1)\)

    对质数\(p,(x_1+x_2+...+x_n)^p\equiv(x_1^p+x_2^p+...+x_n^p)\ (mod\ p)\).

    斐波那契数列

    \(F_i=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^i-(\frac{1-\sqrt5}{2})^i]\)

    \(gcd(F_i,F_j) = F_{gcd(i,j)}\)

    \(F_n^2-F_{n-1}F_{n+1}=(-1)^{n-1}\)

    \(F_1+F_3+...+F_{2n-1}=F_{2n}-F_2+F_1\)

    \(F_2+F_4+...+F_{2n}=F_{2n+1}-F_1\)

    \(F_1^2+F_2^2+...+F_n^2=F_nF_{n+1}\)

    \(F_{2n-2m-2}(F_{2n}+F_{2n+2})=F_{2m+2}+F_{4n-2m}(n > m \geq-1,n\geq1)\)

    \(\frac{F_{2n}}{F_n}=F_{n-1}+F_{n+1}\)

    中国剩余定理

    \[x\equiv{a_i}\pmod{m_i}(1\leq i \leq n)(方程组S) \]

    \(m_1, m_2, ...,m_n\)两两互质,则对任意的\(a_1, a_2, ...,a_n\)同余方程组有解,

    \(M = m_1*m_2*...*m_n\) 是整数\(m_1, m_2, ...,m_n\)的乘积,并设 \(M_i\)是除了\(m_i\)以外的n-1个整数的乘积。

    \(t_i\)\(M_i\)\(m_i\) 意义下的逆元)
    \(M_i * t_i\equiv{1}\pmod{m_i}(1\leq i \leq n)\)
    ​ 方程组 (S) 的通解形式为
    ​ $x = kM + ∑(a_it_i*M_i),i∈{1,2,3,…,n},k∈Z $
    ​ 在模 M 的意义下,方程组(S)只有一个解如下图所示

    \[x = (\sum_{i = 1}^{n}a_i * t_i * M_i) \% M \]

    //求M%A=a,M%B=b,...中的M,其中A,B,C...互质
    int CRT(int a[],int m[],int n){  
        int M = 1;  
        int ans = 0;  
        for(int i=1; i<=n; i++)  
            M *= m[i];  
        for(int i=1; i<=n; i++){  
            int x, y;  
            int Mi = M / m[i];  
            ex_gcd(Mi, m[i], x, y);  
            ans = (ans + Mi * x * a[i]) % M;  
        }  
        if(ans < 0) ans += M;  
        return ans;  
    }  
    
    #include<bits/stdc++.h>/*模不互质*/
    using namespace std;
    int a[10],m[10],tong[103];
    int exgcd(int a,int &x,int b,int &y)
    {
        if(b==0){x=1;y=0;return a;}
        int x2,y2;
        int gcd=exgcd(b,x2,a%b,y2);
        x=y2;
        y=x2-a/b*y2;
        return gcd;
    }
    int main()
    {
        freopen("crt.in","r",stdin);
        freopen("crt.out","w",stdout);
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n;
            scanf("%d",&n);
            scanf("%d%d",&a[1],&m[1]);
            //用lcm求当前到达过的方程中,所有模数的lcm,sum是最后的结果,但是每一次都会用
            //解出的x去更新它,fail判断是否有解 
            int lcm=m[1],sum=a[1],fail=0;
            for(int i=2;i<=n;i++)
            {
                int x,y;
                scanf("%d%d",&a[i],&m[i]);
                a[i]=((a[i]-sum)%m[i]+m[i])%m[i];//sum就是要求的x,不断更新 
                int d=exgcd(lcm,x,m[i],y);//lcm*x+m[i]*y+sum=a[i]才能保证x%m[i]=a[i] 
                if(a[i]%d==0) x=x*(a[i]/d)%m[i];
                else fail=1;
                sum+=x*lcm;//注意是乘lcm哦!不然可能会出现前面的方程冲突 
                lcm=lcm/d*m[i];//到现在这一个方程了,所有模数的最小公倍数 
                sum=(sum%lcm+lcm)%lcm;
            }
            if(fail) printf("No\n");
            else printf("%d\n",sum); 
        }
    }
    

    lucas定理 求 \(C_n^k\% p\)(p为素数,p规模小于1e5)

    表达式:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p。(可以递归)

    递归方程:(C(n%p, m%p)*Lucas(n/p, m/p))%p。(递归出口为m==0,return 1)

    扩展lucas

    输入三个整数\(n,k,p\),输出\(C_n^k\%p\).\(1\leq k \leq n \leq 1e18, 2 \leq p \leq 1e6\),\(p\)不一定是质数。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 100000 + 10;
    typedef long long LL;
    
    LL Pow(LL n, LL m, LL mod) {
        LL ans = 1;
        while(m > 0) {
            if(m & 1) ans = (LL)ans * n % mod;
            n = (LL)n * n % mod; m >>= 1;
        }
        return ans;
    }
    LL Pow(LL n,LL m) {
        LL ans = 1;
        while(m > 0) {
            if(m & 1) ans = ans * n;
            n = n * n; m >>= 1;
        }
        return ans;
    }
    LL x, y;
    LL exgcd(LL a, LL b) {
        if(a == 0) {
            x = 0, y = 1;
            return b;
        }LL r = exgcd(b%a, a);
        LL t = x; x = y - (b/a)*x; y = t;
        return r;
    }
    LL rev(LL a, LL b) { exgcd(a, b); return ((x % b) + b) % b; }
    LL Calc(LL n, LL p, LL t) {
        if(n == 0) return 1;
    
        LL s = Pow(p, t), k = n / s, tmp = 1;
        for(LL i=1; i<=s; i++) if(i % p) tmp = (LL)tmp * i % s;
    
        LL ans = Pow(tmp, k, s);
        for(LL i=s*k + 1; i<=n; i++) if(i % p) ans = (LL)ans * i % s;
    
        return (LL)ans * Calc(n / p, p, t) % s;
    }
    LL C(LL n, LL m, LL p, LL t) {
        LL s = Pow(p, t), q = 0;
        for(LL i=n; i; i/=p) q += i / p;
        for(LL i=m; i; i/=p) q -= i / p;
        for(LL i=n-m; i; i/=p) q -= i / p;
    
        LL ans = Pow(p, q);
        LL a = Calc(n, p, t), b = Calc(m, p, t), c = Calc(n-m, p, t);
        return (LL)(ans * a * rev(b, s) * rev(c, s)) % s;
    }
    LL China(LL A[], LL M[], LL cnt) {
        LL ans = 0, m, n = 1;
        for(LL i=1; i<=cnt; i++) n *= M[i];
        for(LL i=1; i<=cnt; i++) {
            m = n / M[i];
            exgcd(M[i], m);
            ans = (ans + (LL)y * m * A[i]) % n;
        }
        return (ans + n) % n;
    }
    LL A[maxn], M[maxn], cnt;
    LL Lucas(LL n, LL m, LL mod) {
        for(LL i=2; i*i <= mod; i++) if(mod % i == 0) {
            LL t = 0;
            while(mod % i == 0) t++, mod /= i;
            M[++cnt] = Pow(i, t);
            A[cnt] = C(n, m, i, t);
        }if(mod > 1) {
            M[++cnt] = mod;
            A[cnt] = C(n, m, mod, 1);
        }
        return China(A, M, cnt);
    }
    LL n, k, p;
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("input.in", "r", stdin);
        freopen("output.out", "w", stdout);
    #endif
        cin >> n >> k >> p;
        cout << Lucas(n, k, p) << endl;
        return 0;
    }
    

    一些常见的组合数学数

    卡特兰数\(C_n\)

    1, 2, 5, 14, 42,132, 429, 1430, 4862, 16796,58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452

    通项\(C_n = \frac{1}{n + 1} C^{n}_{2n}\)

    性质\(C_n = C^{n}_{2n} - C^{n - 1}_{2n}\) \(C_0 = 1, C_{n+1} = \frac{2(2n+1)}{n+2}C_n\)

    \(C_{n+1} = \sum_{i = 0}^nC_iC_{n - i}\)\(C_n\)是奇数,那么\(n=2^k-1\)

    斯特林数

    第一类Stirling数

    \(s(n,m)\)表示将 n 个不同元素构成m个圆排列的数目。

    无符号第一类斯特林数\(S_u(n,m)\) 带符号第一类斯特林数\(S_s(n, m)\)

    \(x^{n\downarrow}=\prod_{i=0}^{n-1}(x-i)=\sum_{k=0}^ns_s(n,k)x^k\)

    \(x^{n\uparrow}=\prod_{i=0}^{n-1}(x+i)=\sum_{k=0}^ns_u(n,k)x^k\)

    \(s_s(n,m)=(-1)^{n+m}s_u(n,m)\).

    \(s_u(n+1,m)=s_u(n,m-1)+ns_u(n,m)\)

    \(s_s(n+1,m)=s_s(n,m-1)-ns_s(n,m)\)

    \(s_u(0,0)=1,s_u(n,0)=0(n \geq 1), s_u(n,n)=1,s_u(n,1)=(n-1)!\)

    \(s_u(n,n-1)=C_n^2,s_u(n,2)=(n-1)!*\sum_{i=1}^{n-1}\frac{1}{i},s_u(n,n-2)=2C_n^3+3C_n^4\)

    \(\sum_{k=0}^ns_s(n,k)=0^n\),注意\(0^0=1\).

    第二类Stirling数

    \(n\)个不同元素拆分成\(m\)个无区别的集合的方案数

    \(S(n,m)=\frac{1}{m!}\sum_{k=0}^m(-1)^kC_m^k(m-k)^n\)

    \(S(n+1,m)=S(n,m-1)+mS(n,m)\)

    两类数的转化\(\sum_{k=0}^nS(n,k)s(k,m)=\sum_{k=0}^ns(n,k)S(k,m)\)

    贝尔数

    集合划分的数目,如\(B_3=5,\)表示\(\{a,b,c\}\)的五个划分为\(\{\{a\},\{b\}, \{c\}\},\{\{a\}, \{b, c\}\},\{\{b\}, \{a, c\}\},\{\{c\}, \{a, b\}\},\{\{a,b,c\}\}\)

    \(B_0=B_1=1\)

    $1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, 27644437, 190899322, 1382958545, 10480142147, 82864869804, 682076806159, 5832742205057, $$51724158235372, 474869816156751, 4506715738447323, 44152005855084346, 445958869294805289, 4638590332229999353, 49631246523618756274......$

    \(B_{n+1}=\sum\limits_{k=0}^nC_n^kB_k\)

    \(B_n=\frac{1}{e}\sum\limits_{k=0}^{\infin}\frac{k^n}{k!}\)

    \(Touchard\)同余:若\(p\)为质数,则\(B_{p+n}\equiv B_n + B_{n+1}(mod\ p)\).

    \(B_n=\sum\limits_{k=1}^nS(n,k)\),\(S\)是第二类Stirling数.

    常用数论函数

    欧拉函数

    \[\phi(n) = n\prod^{k}_{i = 1}(1 - \frac{1}{p_i})(p_i为n的质因子,相同的质因子只计算一次) \]

    \(\phi(p) = p - 1, \phi(p^k) = (p - 1)*p^{k - 1}\)

    \(时,gcd(m,n) = 1时, \phi(mn) = \phi(m)\phi(n)\)

    \(\sum_{d|n}\phi(d) = n\)

    int getphi(int n)
    {
        int ans = n;
        for (int i = 2; i * i <= n; ++i)
        {
            if (n % i == 0)
            {
                ans -= ans / i;
                while (n % i == 0)
                    n /= i;
            }
        }
        if (n > 1)
            ans -= ans / n;
        return ans;
    }
    

    欧拉定理(n为素数即为费马小定理)

    \(则有gcd(a,n) = 1,则有a^{\phi(n)}\equiv{1}\mod{n}\)

    推广:欧拉降幂

    \[ a^b\equiv\begin{cases} a^{b\%\phi(p)} & gcd(a, p)=1 \\ a^b & gcd(a,p)\not=1, b<\phi(p) \\ a^{b\%\phi(p)+\phi(p)} & gcd(a,p)\not=1, b\geq\phi(p) \end{cases}\]

    LL Mod(LL b, LL mod){return b < mod?b:b%mod+mod;}
    

    莫比乌斯函数

    n质因数分解后,\(如果有幂次大于的为质因子个数\mu(1) = 1;\mu(n) = 0如果有幂次大于1的;\mu(n) = (-1)^k,k为质因子个数\)

    n>2时,n的所有约数对应的莫比乌斯函数值之和为0 即\(\sum_{d|n}\mu(d) = 0\)

    \(\sum_{d|n}\frac{\mu(d)}{d} = \frac{\phi(n)}{n}\)

    等价于\(\phi(n)=\sum_{d|n}\frac{n}{d}\mu(d)=\sum_{d|n}d\mu(\frac{n}{d})\)

    \([gcd(a,b) == 1] = \sum_{d|gcd(a,b)}\mu(d)=\sum_{d|a,d|b}\mu(d)\) 容斥

    例题:求1-n中有多少个数和x互质

    \(\sum_{d|x}\mu(d)*\lfloor\frac{n}{d}\rfloor\)容斥系数即为莫比乌斯函数

    int mobius(int x)
    {
        int res=1;
        int i,j;
        for(i=2;i*i<=x;i++)
            if(x%i==0)
            {
                x/=i;
                if(x%i==0) mu[i]=0;
                else mu[i]*=-1;
                while(x%i==0) x/=i;
            }
        return res;
    }
    

    exgcd

    #include <iostream>
    #define LL long long
    using namespace std;
    
    LL exgcd(LL a, LL b, LL &p, LL &q)
    {
        if (a == 0 && b == 0) return -1;
        if (b == 0)
        {
            p = 1; q = 0; return a;
        }
        LL d = exgcd(b, a % b, q, p);
        q -= a / b * p;
        return d;
    }
    

    逆元

    1.费马小定理----mod为素数

    qp(a, mod - 2);

    2.exgcd

    给定模数m,求a的逆相当于求解ax=1(mod m),这个方程可以转化为ax-my=1 , 然后套用求二元一次方程的方法,用扩展欧几里得算法求得一组x0,y0和gcd
    检查gcd是否为1 ,gcd不为1则说明逆元不存在
    若为1,则调整x0到0~m-1的范围中即可

    #define LL long long
    void extgcd(LL a,LL b,LL &d,LL &x,LL &y)
    {
        if(!b){ d=a; x=1; y=0;}
        else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); }
    }
    LL inv(LL a,LL n)
    {
        LL d,x,y;
        extgcd(a,n,d,x,y);
        return d==1?(x+n)%n:-1;
    }
    
    

    3.b|a

    \[\frac{a}{b}\%mod = \frac{a\%(mod*b)}{b} \]

    4.欧拉定理(m可以不为质数,但要a和m互质)

    \[inv(a) = a^{\phi(mod) - 1}\%mod \]

    5.逆元打表

    \[inv[i] = (M - \lfloor\frac{M}{i}\rfloor) * inv[M\%i]\%M \]

    #define LL long long
    const int N = 1e5 + 5;
    int inv[N];
    void getinv(int n, int p) 
    {
        inv[1] = 1;
        for (int i = 2; i <= n; ++i) 
        {
            inv[i] = (LL) (p - p / i) * inv[p%i] % p;
        }
    }
    

    线性筛(素数筛,欧拉函数,莫比乌斯函数,约数个数,最小因子)

    #define MAXN 1000000
    int prime[MAXN + 8];
    bool notprime[MAXN + 8];
    //int phi[MAXN + 8];
    ///int mu[MAXN + 8];
    ////int facnum[MAXN + 8], d[MAXN + 8];
    int ind = 0;
    void getprime()
    {
        //phi[1] = 1;
        ///mu[1] = 1;
        ////facnum[1] = 1;
        for (int i = 2; i <= MAXN; ++i)
        {
            if(!notprime[i])
            {
                prime[++ind] = i;
                //phi[i] = i - 1;
                ///mu[i] = -1;
                ////facnum[i] = 2, d[i] = 1;
                /////minfactor[i] = i;
            }
            for (int j = 1; j <= ind && i * prime[j] <= MAXN; ++j)
            {
                notprime[i * prime[j]] = 1;
                if(i % prime[j] == 0)
                {
                    //phi[i * prime[j]] = phi[i] * prime[j];
                    ///mu[i * prime[j]] = 0;
                    ////facnum[i * prime[j]] = facnum[i] / (d[i] + 1) * (d[i] + 2), d[i * prime[j]] = d[i] + 1;
                    break;
                }
                //phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                ///mu[i * prime[j]] = -mu[i];
                ////facnum[i * prime[j]] = facnum[i] * 2, d[i * prime[j]] = 1;
                /////minfactor[i*prime[j]] = prime[j];
            }
        }
    }
    

    例子:前n个数的约数个数筛

    facnum[i]表示i的约数个数

    通过素数筛得到前n个数的约数个数非常巧妙,首先根据约数个数定理:

    对于一个大于1正整数n可以分解质因数:\(n = p_1^{d_1} * p_2^{d_2} *... * p_k^{d_k}\),其中pi为素数

    则n的正约数的个数就是

    :facnum[n] = (1 + d1) * (1 + d2) * ... * (1 + dk)
    我们需要一个辅助数组d[i],表示i的最小质因子的次幂,(最小的原因是素数筛里每次都是用最小的质因子来筛合数的),还是三种情况:
    1.当i为素数时,facnum[i] = 2;d[i] = 1,很好理解
    2.当i % p[j] != 0时,gcd(i, p[j]) =1,由积性函数的性质可得facnum[i * p[j]] = facnum[i] * facnum[p[j]] = facnum[i] * 2
    ​ d[i * p[j]] = 1(无平方因子)
    3.当i % p[j] == 0时,出现平方因子,最小质因子的次幂加1,因此有

    facnum[i * p[j]] = facnum[i] / (d[i] + 1) * (d[i] + 2)
    ​ d[i * p[j]] = d[i] + 1

    康托展开

    康托展开

    康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

    \[X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! \]

    在(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值。

    首位是3,则小于3的数有两个,为1和2,a[5]=2,则首位小于3的所有排列组合为 a[0]*(5-1)!

    第二位是4,则小于4的数有两个,为1和2,注意这里3并不能算,因为3已经在第一位,所以其实计算的是在第二位之后小于4的个数。因此a[4]=2

    第三位是1,则在其之后小于1的数有0个,所以a[3]=0

    第四位是5,则在其之后小于5的数有1个,为2,所以a[2]=1

    最后一位就不用计算啦,因为在它之后已经没有数了,所以a[1]固定为0

    根据公式:
    X = 2 * 4! + 2 * 3! + 0 * 2! + 1 * 1! + 0 * 0! = 2 * 24 + 2 * 6 + 1 = 61
    所以比 34152 小的组合有61个,即34152是排第62。

    static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘
    int cantor(int *a, int n)
    {
        int x = 0;
        for (int i = 0; i < n; ++i) {
            int smaller = 0;  // 在当前位之后小于其的个数
            for (int j = i + 1; j < n; ++j) {
                if (a[j] < a[i])
                    smaller++;
            }
            x += FAC[n - i - 1] * smaller; // 康托展开累加
        }
        return x;  // 康托展开值
    } 
    

    逆康托展开

    一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。即对于上述例子,在(1,2,3,4,5)给出61可以算出起排列组合为 34152。由上述的计算过程可以容易的逆推回来,具体过程如下:

    用 61 / 4! = 2余13,说明a[5]=2,说明比首位小的数有2个,所以首位为3。

    用 13 / 3! = 2余1,说明a[4]=2,说明在第二位之后小于第二位的数有2个,所以第二位为4。

    用 1 / 2! = 0余1,说明a[3]=0,说明在第三位之后没有小于第三位的数,所以第三位为1。

    用 1 / 1! = 1余0,说明a[2]=1,说明在第二位之后小于第四位的数有1个,所以第四位为5。

    最后一位自然就是剩下的数2啦。

    通过以上分析,所求排列组合为 34152。

    static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘
    void decantor(int x, int n)
    {
        vector<int> v;  // 存放当前可选数
        vector<int> a;  // 所求排列组合
        for(int i=1;i<=n;i++)
            v.push_back(i);
        for(int i=m;i>=1;i--)
        {
            int r = x % FAC[i-1];
            int t = x / FAC[i-1];
            x = r;
            sort(v.begin(),v.end());// 从小到大排序 
            a.push_back(v[t]);      // 剩余数里第t+1个数为当前位
            v.erase(v.begin()+t);   // 移除选做当前位的数
        }
    }
    

    莫比乌斯反演+数论分块

    如果存在函数F(x)和f(x),满足 \(F(n) = \sum_{d|n}f(d)\),那么

    \[f(n) = \sum_{d|n}\mu(d)F(\frac{n}{d}) \]

    或如果\(F(n) = \sum_{n|d}f(d)\),那么

    \[f(n) = \sum_{n|d}\mu(\frac{d}{n})F(d) \]

    例题:求对于区间[a,b]内的整数x和[c, d]内的y,满足gcd(x,y)=k的数对的个数。

    \(的对数F(t):gcd(x, y) \% t == 0 的(x,y)对数\) \(的对数f(t):gcd(x, y) == t 的(x, y)对数\)

    考虑 $x \in [1,b] $ and $ y \in [1,d]$ : \(F(t) = \lfloor\frac{b}{t}\rfloor\lfloor\frac{d}{t}\rfloor\)

    $Ans = f(k), gcd(x,y) = k \leftrightarrow gcd(x/k, y/k) = 1 \leftrightarrow $

    \(gcd(x,y) = 1(x\in[1,b/k]\) and \(y\in[1,d/k])\)

    因此直接把b,d都除上k,\(f(1) = \sum^{min(b/k,d/k)}_{i = 1}\mu(i)F(i)\)就是结果

    当t接近最大值时,对一段连续的t它们的\(F(t)\)值都相同,因此可以打出莫比乌斯前缀和然后分块求解。

    #include <bits/stdc++.h>
    #define LL long long
    #define pb push_back
    #define MAXN 50000
    using namespace std;
    
    bool check[MAXN + 8];
    int prime[MAXN + 8];
    int mu[MAXN + 8];
    int sum[MAXN + 8];
    void Mobius()
    {
        memset(check, false, sizeof(check));
        mu[1] = 1;
        int tot = 0;
        for (int i = 2; i <= MAXN; ++i)
        {
            if (!check[i])
            {
                prime[++tot] = i;
                mu[i] = -1;
            }
            for (int j = 1; j <= tot; ++j)
            {
                if (i * prime[j] > MAXN)
                    break;
                check[i * prime[j]] = true;
                if (i % prime[j] == 0)
                {
                    mu[i * prime[j]] = 0;
                    break;
                }
                else
                {
                    mu[i * prime[j]] = -mu[i];
                }
            }
        }
        for (int i = 1; i <= MAXN; ++i)
        {
            sum[i] = sum[i - 1] + mu[i];
        }
    }
    
    LL calc(int a, int b)
    {
        LL ret = 0;
        int last = 0;
        for (int i = 1; i <= min(a, b); i = last + 1)
        {
            last = min(a / (a / i), b / (b / i));
            ret += 1LL * (sum[last] - sum[i - 1]) * (a / i) * (b / i);
        }
        return ret;
    }
    int main()
    {
        Mobius();
        int T, a, b, c, d, k;
        cin >> T;
        for (int t = 1; t <= T; ++t)
        {
            cin >> a >> b >> c >> d >> k;
            a--, c--;
            cout << calc(b / k, d / k) - calc(a / k, d / k) - calc(b / k, c / k) + calc(a / k, c / k);//容斥
            if (t != T) cout << endl;
        }
        return 0;
    }
    

    卷积

    \(id(n)=n,\epsilon(n)=[n=1],I(n)=1,d(n)=\sum_{d|n}1,\sigma(n)=\sum_{d|n}d,\lambda(n)=(-1)^k\)

    \(\mu*I=\epsilon,\phi*I=id,\phi=id*\mu,d=I*I,1=\mu*d,\sigma*\mu=id,\sigma=id*I\)

    \(\epsilon\)卷任何\(f\)都为\(f\)

    常见求和公式及其推导

    1.\(\sum_{a=1}^n\sum_{b=1}^mgcd(a,b)=\sum_{d=1}^{min(n,m)}\phi(d)\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\)

    不妨设\(n\leq m\),枚举\(d=gcd(a,b)\),反演得到左边=\(\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}d\mu(i)\lfloor\frac{n}{id}\rfloor\lfloor\frac{m}{id}\rfloor\),变形,用\(t\)替换\(i*d\),得\(\sum_{t=1}^n\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor\sum_{d|t}d\mu(\frac{t}{d})\),后面那个式子是典型卷积,所以原公式成立。

    2.\(\sum_{a=1}^n\sum_{b=1}^mlcm(a,b)=\frac{1}{4}\sum_{d=1}^{min(n,m)}d\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor(\lfloor\frac{n}{d}\rfloor+1)(\lfloor\frac{m}{d}\rfloor+1)\sum_{i|d}i\mu(i)\)

    反演:设\(F(t)=\sum_{i=1}^n\sum_{j=1}^mij[t|gcd(i,j)],f(t)=\sum_{i=1}^n\sum_{j=1}^mij[t=gcd(i,j)]\),则有\(F(t)=\sum_{i=1}^n\sum_{j=1}^mij[t|i][t|j]=\frac{\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor(\lfloor\frac{n}{t}\rfloor+1)(\lfloor\frac{m}{t}\rfloor+1)}{4}\).因此答案=\(\sum_{d=1}^nd\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(i)i^2\frac{\lfloor\frac{n}{id}\rfloor\lfloor\frac{m}{id}\rfloor(\lfloor\frac{n}{id}\rfloor+1)(\lfloor\frac{m}{id}\rfloor+1)}{4}\).\(t=id\)来替换\(d\),套路求解。记\(G(d)=\sum_{i|d}i\mu(i)\),显然是积性函数,对质数\(p\)\(G(p^k)=1-p\),随便推一推就能用线性筛预处理,前面再一分块,就能\(O(\sqrt n)\)求解。

    3.\(\sum_{a=1}^n\sum_{b=1}^nlcm(a,b)=\sum_{i=1}^n(-i+2\sum_{j=1}^ilcm(i,j))\).

    预处理,O(1)输出。

    4.\(\sum_{i=1}^ngcd(i,n)=\sum_{d|n}\frac{n}{d}\phi(d)\).

    枚举\(d\),老套路了。

    5.\(\sum_{i=1}^n\sigma(i)=\sum_{i=1}^ni\lfloor\frac{n}{i}\rfloor\).

    根据\(\sigma\)的定义轻松得出。左边=\(\sum_{i=1}^n\sum_{j|i}j=\sum_{j=1}^nj\sum_{i=1}^{\lfloor\frac{n}{j}\rfloor}1\).

    一类数论函数的更快求和方法

    杜教筛---\(O(n^{2/3})\)计算一类积性函数的前缀和

    我们要求\(\sum_{i=1}^nf(i)\),其中\(f\)为积性函数。

    构造两个积性函数\(g,h\)满足\(h=f*g\)

    \(\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f(i/d)=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)=\sum_{d=1}^ng(d)S_f(\lfloor\frac{n}{d}\rfloor)\)

    因此\(g(1)S_f(n)=\sum_{i=1}^{n}h(i)-\sum_{i=2}^ng(d)S_f(\lfloor\frac{n}{d}\rfloor)\)

    包含\(\phi(i),\mu(i),i\phi(i),i\mu(i)\)的板子
    #include <bits/stdc++.h>
    #define MAXN 3000000
    #define LL long long
    using namespace std;
    
    const int mod = 1000000007;
    const int inv2 = 500000004;
    const int inv4 = 250000002;
    const int inv6 = 166666668;
    LL qp(LL a, LL b)
    {
        LL ret = 1;
        while (b)
        {
            if (b & 1)
                ret = ret * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ret;
    }
    
    LL inv(LL a)
    {
        return qp(a, mod - 2);
    }
    int prime[MAXN + 8];
    bool notprime[MAXN + 8];
    LL phi[MAXN + 8], phisum[MAXN + 8];
    LL mu[MAXN + 8], musum[MAXN + 8];
    LL iphisum[MAXN + 8];
    LL imusum[MAXN + 8];
    int ind = 0;
    void getprime()
    {
        phi[1] = 1;
        mu[1] = 1;
        for (int i = 2; i <= MAXN; ++i)
        {
            if(!notprime[i])
            {
                prime[++ind] = i;
                phi[i] = (i - 1) % mod;
                mu[i] = -1;
            }
            for (int j = 1; j <= ind && i * prime[j] <= MAXN; ++j)
            {
                notprime[i * prime[j]] = 1;
                if(i % prime[j] == 0)
                {
                    phi[i * prime[j]] = phi[i] * prime[j] % mod;
                    mu[i * prime[j]] = 0;
                    break;
                }
                phi[i * prime[j]] = phi[i] * (prime[j] - 1) % mod;
                mu[i * prime[j]] = -mu[i];
            }
        }
        for (int i = 1; i <= MAXN; ++i)
        {
            phisum[i] = (phisum[i - 1] + phi[i]) % mod;
            musum[i] = (musum[i - 1] + mu[i] + mod) % mod;
            iphisum[i] = (iphisum[i - 1] + i * phi[i] % mod) % mod;
            imusum[i] = (imusum[i - 1] + i * mu[i] % mod + mod) % mod;
        }
    }
    unordered_map<LL,LL> _phi, _mu, _iphi, _imu;
    LL Calcphi(LL n)
    {
        if (n <= MAXN)
            return phisum[n];
        auto it = _phi.find(n);
        if (it != _phi.end())
            return it->second;
        LL last;
        LL ans = (n % mod) * (((n % mod) + 1 + mod) % mod) % mod * inv2 % mod;
        for (LL i = 2; i <= n; i = last + 1)
        {
            last = (n / (n / i));
            ans = (ans - (((last % mod) - (i % mod) + 1 + mod) % mod * (Calcphi(n / i) % mod) % mod) + mod) % mod;
        }
        return _phi[n] = ans;
    }
    
    LL Calciphi(LL n)
    {
        if (n <= MAXN)
            return iphisum[n];
        auto it = _iphi.find(n);
        if (it != _iphi.end())
            return it->second;
        LL ret = n * (n + 1) % mod * (2 * n + 1) % mod * inv6 % mod;
        LL last;
        for (LL i = 2; i <= n; i = last + 1)
        {
            last = n / (n / i);
            ret = (ret - ((last + i) % mod * (last - i + 1) % mod * inv2 % mod + mod) % mod * Calciphi(n / i) % mod + mod) % mod;
        }
        return _iphi[n] = ret;
    }
    
    LL Calcmu(LL n)
    {
        if (n <= MAXN)
            return musum[n];
        auto it = _mu.find(n);
        if (it != _mu.end())
            return it->second;
        LL ans = 1;
        LL last;
        for (LL i = 2; i <= n; i = last + 1)
        {
            last = n / (n / i);
            ans = (ans - (last - i + 1) * Calcmu(n / i) % mod + mod) % mod;
        }
        return _mu[n] = ans;
    }
    
    LL Calcimu(int n)
    {
        if (n <= MAXN)
            return imusum[n];
        auto it = _imu.find(n);
        if (it != _imu.end())
            return it->second;
        int last;
        LL ret = 1;
        for (int i = 2; i <= n; i = last + 1)
        {
            last = n / (n / i);
            ret = (ret - ((1LL * last + i) % mod * (last - i + 1) % mod * inv2 % mod * Calcimu(n / i) % mod) + mod) % mod;
        }
        return _imu[n] = ret;
    }
    
    

    洲阁筛(还没学)

    简单的写了个求1~n质数个数 -O2 N=1e11要2.5s

    #pragma GCC optimize(2)
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    int L0[1000005],L1[1000005],sn,m,P[1000005],x,ga[1000005],gb[1000005];
    LL n,a[1000005],b[1000005];
    int main(){
        n=100000000000;
    //    scanf("%lld",&n);
        sn=sqrt(n);
        for (int i=2;i<=sn;++i){
            if (!P[i]) P[++m]=i;
            for (int j=1;j<=m;++j){
                x=P[j]*i;
                if (x>sn) break;
                P[x]=1;
                if (i%P[j]==0) break;
            }
        }
        for (int i=1;i<=sn;++i) a[i]=i,b[i]=n/i;
        for (int i=1,j=1;i<=sn;++i){
            while (j<=m&&P[j]*P[j]<=i) ++j;
            L0[i]=j;
        }
        for (int i=sn,j=L0[sn];i;--i){
            while (j<=m&&P[j]*P[j]<=b[i]) ++j;
            L1[i]=j;
        }
        for (int i=1;i<=m;++i){
            for (int j=1;j<=sn&&i<L1[j];++j){
                LL y=j*P[i]; gb[j]=i;
                if (y<=sn) b[j]-=b[y]+gb[y]+1-i;
                else b[j]-=a[n/y]+ga[n/y]+1-i;
            }
            for (int j=sn;j&&i<L0[j];--j){
                LL y=j/P[i]; ga[j]=i;
                a[j]-=a[y]+ga[y]+1-i;
            }
        }
        printf("%lld\n",b[1]+m-1);
        return 0;
    }
    

    min25筛(学了一半)

    待咕

    类欧几里得算法

    可快速计算:

    \(f(a,b,c,n)=\sum\limits_{i=0}^n\lfloor\frac{ai+b}{c}\rfloor\)

    \(g(a,b,c,n)=\sum\limits_{i=0}^ni\lfloor\frac{ai+b}{c}\rfloor\)

    \(h(a,b,c,n)=\sum\limits_{i=0}^n\lfloor\frac{ai+b}{c}\rfloor^2\)

    常用推导公式

    \(a\leq \lfloor\frac{b}{c}\rfloor⇔ac\leq b\)

    \(a< \lceil\frac{b}{c}\rceil⇔ac< b\)

    \(a\geq \lceil\frac{b}{c}\rceil⇔ac\geq b\)

    \(a> \lfloor\frac{b}{c}\rfloor⇔ac> b\)

    \(\lfloor\frac{b}{c}\rfloor=\lfloor\frac{b+c-1}{c}\rfloor=\lceil\frac{b-c+1}{c}\rceil\)

    以f的推导过程为例

    1. \(a\geq c\)\(b \geq c\)

    \(\lfloor\frac{ai+b}{c}\rfloor=\lfloor\frac{(a\%c)i+b\%c}{c}\rfloor+\lfloor\frac{a}{c}\rfloor i+\lfloor\frac{b}{c}\rfloor\)

    因此\(f(a,b,c,n)=f(a\%c,b\%c,c,n)+\frac{n(n+1)}{2}\lfloor\frac{a}{c}\rfloor+(n+1)\lfloor\frac{b}{c}\rfloor\)

    2.\(a<c\)\(b<c\)

    \(a=0:f(a,b,c,n)=0\)

    否则:

    \[f(a,b,c,n)=\sum_{i=0}^n\sum_{j=0}^{\lfloor\frac{ai+b}{c}\rfloor -1}1 \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}\sum_{i=0}^n[j<\lfloor\frac{ai+b}{c}\rfloor] \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}\sum_{i=0}^n[j<\lceil\frac{ai+b-c+1}{c}\rceil] \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}\sum_{i=0}^n[cj<ai+b-c+1] \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}\sum_{i=0}^n[ai>cj-b+c-1] \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}\sum_{i=0}^n[i>\lfloor\frac{cj-b+c-1}{c}\rfloor] \\=\sum_{j=0}^{\lfloor\frac{an+b}{c}\rfloor -1}n-\lfloor\frac{cj-b+c-1}{c}\rfloor \\=n\lfloor\frac{an+b}{c}\rfloor-f(c,-b+c-1,a,\lfloor\frac{an+b}{c}\rfloor-1) \]

    \(或g(a,b,c,n)=g(a\%c,b\%c,c,n)+\frac{n(n+1)(2n+1)}{6}\lfloor\frac{a}{c}\rfloor+\frac{n(n+1)}{2}\lfloor\frac{b}{c}\rfloor\ (a\geq c或b\geq c)\)

    \(否则g(a,b,c,n)=\frac{\lfloor\frac{an+b}{c}\rfloor n(n+1)-f(c,-b+c-1,a,\lfloor\frac{an+b}{c}\rfloor-1)-h(c,-b+c-1,a,\lfloor\frac{an+b}{c}\rfloor-1)}{2}(否则)\)

    \(或h(a,b,c,n)=h(a\%c,b\%c,c,n)+\frac{n(n+1)(2n+1)}{6}\lfloor\frac{a}{c}\rfloor^2+(n+1)\lfloor\frac{b}{c}\rfloor^2+2\lfloor\frac{b}{c}\rfloor f(a\%c,b\%c,c,n)\\+2\lfloor\frac{a}{c}\rfloor g(a\%c,b\%c,c,n)+\lfloor\frac{a}{c}\rfloor\lfloor\frac{b}{c}\rfloor n(n+1)(a\geq c或b\geq c)\)

    \(否则h(a,b,c,n)=\lfloor\frac{an+b}{c}\rfloor(\lfloor\frac{an+b}{c}\rfloor+1)n-2g(c,-b+c-1,a,\lfloor\frac{an+b}{c}\rfloor-1)\\-2f(c,-b+c-1,a,\lfloor\frac{an+b}{c}\rfloor-1)-f(a,b,c,n)(否则)\)

    /*洛谷P5170,输入T组,一开始写了个递归暴力,给我T飞掉了,我人都傻了*/
    #include <bits/stdc++.h>
    #define LL long long
    #define MAXN 2000
    #define x first
    #define y second
    #define pii pair<long long, long long>
    using namespace std;
    const LL mod = 998244353;
    const LL inv2 = 499122177;
    const LL inv6 = 166374059;
    const LL inv4 = 748683265;
    struct Node
    {
        LL f, g, h;
        Node(){}
        Node(LL _f, LL _g, LL _h)
        {
            f = _f; g = _g; h = _h;
        }
    };
    
    Node solve(LL a, LL b, LL c, LL n)
    {
        if (a == 0)
        {
            n %= mod;
            LL bc = (b/c) % mod;
            return Node((n+1)*bc%mod, n*(n+1)%mod*inv2%mod*bc%mod, (n+1)*bc%mod*bc%mod);
        }
        LL t = (a*n+b)/c;
        LL ac=(a/c)%mod, bc=(b/c)%mod;
        if (a >= c || b >= c)
        {
            Node n1 = solve(a%c,b%c,c,n);
            return Node(( n1.f + n*(n+1)%mod*inv2%mod*ac%mod + (n+1)*bc%mod) % mod,
                        ( n1.g + n*(n+1)%mod*(2*n+1)%mod*inv6%mod*ac%mod % mod + n*(n+1)%mod*inv2%mod*bc%mod) % mod,
                        ( n1.h + n*(n+1)%mod*(2*n+1)%mod*inv6%mod*ac%mod*ac%mod + (n+1)*bc%mod*bc%mod + 2*bc%mod*n1.f%mod + 2*ac%mod*n1.g%mod + ac*bc%mod*n%mod*(n+1)%mod) % mod
                        );
        }
        else
        {
            Node n2 = solve(c,c-b-1,a,t-1);
            t %= mod;
            return Node((n * t % mod - n2.f + mod) % mod,
                        inv2 * ((t*n%mod*(n+1)%mod - n2.f - n2.h + 2*mod) % mod) % mod,
                        (t*(t+1)%mod*n%mod - 2*n2.g%mod - 2*n2.f%mod - (n * t % mod - n2.f + mod) % mod + 4*mod) % mod
                        );
        }
    }
    
    int main()
    {
        LL a, b, c, n;
        int T;
        scanf("%d", &T);
        while (T--)
        {
            scanf("%lld %lld %lld %lld", &n, &a, &b, &c);
            Node ans = solve(a, b, c, n);
            printf("%lld %lld %lld\n", ans.f, ans.h, ans.g);
        }
    	return 0;
    }
    
    

    拉格朗日插值法

    朴素算法

    给定\(k+1\)个点\((x_0,y_0),...,(x_k,y_k)\)表示的\(k\)次多项式\(L(x)\),给出\(a\)\(L(a)\).

    \(\ell_j(x)=\prod\limits_{i=0,i\neq j}^k\frac{x-x_i}{x_j-x_i}\),则有\(L(x)=\sum\limits_{j=0}^ky_j\ell_j(x)\). \(O(n^2)\).

    /*洛谷P4781 给出n,X,接下来给出n对(x,y),求L(X).*/
    #include <bits/stdc++.h>
    #define LL long long
    #define MAXN 2000
    #define x first
    #define y second
    #define pii pair<long long, long long>
    using namespace std;
    const int mod = 998244353;
    int n;
    LL X;
    pii arr[MAXN + 8];
    
    LL qp(LL a, LL b)
    {
        LL ret = 1;
        while (b)
        {
            if (b & 1)
                ret = ret * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ret;
    }
    
    LL inv(LL a)
    {
        return qp(a, mod - 2);
    }
    
    LL calcell(int j, LL X)
    {
        LL ret = 1;
        for (int i = 1; i <= n; ++i)
        {
            if (i == j || arr[i].x == arr[j].x)
                continue;
            ret = ret * (X - arr[i].x + mod) % mod * inv((arr[j].x - arr[i].x + mod) % mod) % mod;
        }
        return ret;
    }
    
    LL calcF(LL X)
    {
        LL ret = 0;
        for (int i = 1; i <= n; ++i)
        {
            ret = (ret + arr[i].y * calcell(i, X) % mod) % mod;
        }
        return ret;
    }
    
    int main()
    {
        cin >> n >> X;
        for (int i = 1; i <= n; ++i)
        {
            cin >> arr[i].x >> arr[i].y;
        }
        cout << calcF(X) << endl;
    	return 0;
    }
    
    

    重心拉格朗日插值法

    插入新点时单次插入优化至\(O(n)\):

    \(\ell(x)=\prod\limits_{i=0}^k(x-x_i)\),\(w_j=\frac{1}{\prod_{i=0,i\neq j}^k(x_j-x_i)}\),则\(\ell_j(x)=\ell(x)\frac{w_j}{x-x_j}\).

    \(L(x)=\ell(x)\sum\limits_{j=0}^k\frac{w_j}{x-x_j}y_j\).

    对多项式\(g(x)=1\)插值,有\(\forall x,g(x)=\ell(x)\sum\limits_{j=0}^k\frac{w_j}{x-x_j}\),因此\(L(x)\)两边同除以\(g(x)\),有

    \(L(x)=\frac{\sum_{j=0}^k\frac{w_j}{x-x_j}y_j}{\sum_{j=0}^k\frac{w_j}{x-x_j}}\).插入新值\(x_{k+1}\)时,只需对\(w_j\)乘上\(x_j-x_{k+1}\)即可更新\(w\),更新所有\(w\)总共需要\(O(n)\).

    /*依然是上面那个模板题*/
    #include <bits/stdc++.h>
    #define LL long long
    #define MAXN 2000
    #define x first
    #define y second
    #define pii pair<long long, long long>
    using namespace std;
    const int mod = 998244353;
    int n;
    LL X;
    pii arr[MAXN + 8];
    LL w[MAXN + 8];
    LL qp(LL a, LL b)
    {
        LL ret = 1;
        while (b)
        {
            if (b & 1)
                ret = ret * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return ret;
    }
    
    LL inv(LL a)
    {
        return qp(a, mod - 2);
    }
    
    void insert(int i)
    {
        w[i] = 1;
        for (int j = 1; j < i; ++j)
        {
            if (arr[i].x == arr[j].x)
                continue;
            w[i] = w[i] * (arr[i].x - arr[j].x + mod) % mod;
            w[j] = w[j] * inv(arr[j].x - arr[i].x + mod) % mod;
        }
        w[i] = inv(w[i]);
        return;
    }
    
    LL query(int n)
    {
        LL fz = 0, fm = 0;
        for (int i = 1; i <= n; ++i)
        {
            fz = (fz + w[i] * inv((X - arr[i].x + mod) % mod) % mod * arr[i].y % mod) % mod;
            fm = (fm + w[i] * inv((X - arr[i].x + mod) % mod) % mod) % mod;
        }
        return fz * inv(fm) % mod;
    }
    
    int main()
    {
        cin >> n >> X;
        for (int i = 1; i <= n; ++i)
        {
            cin >> arr[i].x >> arr[i].y;
            insert(i);
        }
        cout << query(n) << endl;
    	return 0;
    }
    
    

    佩尔方程

    \(x^2-Dy^2=1,D\)不是平方数。

    \((x_1,y_1)\)是使\(x_1\)最小的解,则通解为

    \(x_k+\sqrt{D}y_k=(x_1+\sqrt{D}y_1)^k\).

    \(x^2-Dy^2=1,D\)不是平方数。

    \((x_0,y_0)\)是使\(x_0\)最小的解,则通解为

    \(x_k+\sqrt{D}y_k=(x_0+\sqrt{D}y_0)^{2k+1}\).

    佩尔方程(\(x^2-Dy^2=1\))的最小正整数解

    bool PQA(LL D, LL &p, LL &q) {//来自于PQA算法的一个特例
        const double eps = 1e-6;
        LL d = sqrt(D);
        if (abs(d * d - D) <= eps)
            return false;//这里是判断佩尔方程有没有解
        LL u = 0, v = 1, a = int(sqrt(D)), a0 = a, lastp = 1, lastq = 0;
        p = a, q = 1;
        do {
            u = a * v - u;
            v = (D - u * u) / v;
            a = (a0 + u) / v;
            LL thisp = p, thisq = q;
            p = a * p + lastp;
            q = a * q + lastq;
            lastp = thisp;
            lastq = thisq;
        } while ((v != 1 && a <= a0));//这里一定要用do~while循环
        p = lastp;
        q = lastq;
        //这样求出后的(p,q)是p2 – D * q2 = (-1)k的解,也就是说p2 – D * q2可能等于1也可能等于-1,如果等于1,(p,q)就是解,如果等于-1还要通过(p2 + D * q2,2 * p * q)来求解,如下
        if (p * p - D * q * q == -1) {
            p = lastp * lastp + D * lastq * lastq;
            q = 2 * lastp * lastq;
        }
        return true;
    }
    

    正整数的拆分方案数\(O(n\sqrt{n})\)

    void init() {
        for(int i = 0; i < 1000; ++i) {
            g[i << 1] = 1LL * i * (i * 3 - 1) / 2;
            g[i << 1 | 1] = 1LL * i * (i * 3 + 1) / 2;
        }
        f[0] = 1, f[1] = 1, f[2] = 2;
        for(int i = 3; i < N; ++i) {
            f[i] = 0;
            int k = 0;
            for(int j = 2; g[j] <= i; ++j) {
                if(k & 2) 
                    f[i] = ((f[i] - f[i - g[j]]) % mod + mod) % mod;
                else
                    f[i] = ((f[i] + f[i - g[j]]) % mod + mod) % mod;
                k++; k %= 8;
            }
        }
    }
    /*g是五边形数,f是拆分方案数*/
    /*若限制每个数大于等于3,则 ans=f[i]+f[i-3]-f[i-1]-f[i-2]*/
    

    博弈论

    常用简单博弈模型

    巴什博弈

    一堆n个石子,两人轮流取,每次最多取m个,取完最后一颗的人胜。

    解:\(n\%(m+1) = 0\)时,先手必败,否则必胜。

    斐波那契博弈

    一堆n个石子,先手取任意多个,但不能取完,之后每次取的石子数只能在1到对手刚取的石子数的2倍之间(闭区间),取完最后一颗的人胜。

    解:\(n\)是斐波那契数时,先手必败,否则必胜。

    威佐夫博弈

    两堆各若干个石子,每次可以一堆取任意多个,或两堆取同样多个,取完最后一颗的胜。

    解:\((0,0),(1,2),(3,5),(4,7),(6,10)....\)先手必败。一般地,对奇异局势(先手必败局势)\((a,b)(a\le b),\)有$a=\lfloor(b-a)\frac{\sqrt 5 + 1}{2}\rfloor $

    或者说,第\(k\)组奇异局势\((a_k,b_k)\)\(a_k=\lfloor\frac{\sqrt 5 + 1}{2}k\rfloor, b_k=a_k +k\).

    尼姆博弈

    若干堆石子,每堆若干个,每次选一堆取至少一个,取完最后一颗的胜。

    解:\(\bigoplus_{i=1}^n a[i] = 0\)时,先手必败,否则必胜。

    阶梯博弈

    若干堆石子,堆标号依次\(1,2,3,...,n\)每次选一堆(标号为\(i\))的若干个放到标号\(i-1\)的堆(可以放到标号为0的堆,称作地面),把最后一颗石子放到\(0\)号堆的胜。

    解:等价于保留奇数号,忽视偶数号的Nim博弈。

  • 相关阅读:
    MyEclipse的优化
    关于学习Hadoop中未总结的资料
    OSChina 的 Tomcat 配置 server.xml
    Linux 定时任务 Crontab命令 详解
    常用Linux命令收集
    SQL练习 高级子查询
    JAVA设计模式中的单例模式
    Linux课程笔记 Rsync数据同步服务
    Linux课程笔记 SSH介绍
    Linux课程笔记 Raid技术介绍
  • 原文地址:https://www.cnblogs.com/zhugezy/p/10970021.html
Copyright © 2011-2022 走看看