zoukankan      html  css  js  c++  java
  • [bzoj1951][SDOI2010]古代猪文

    题目描述:

    计算(G^{sum_{i|n} C(n,i)} \% P)

    题目解答:

    为叙述方便起见,我们令sigma()为W
    显然W是一个非常大的数,如果暴力直接计算不是明智的选择。
    我们这里使用费马小定理:
    对于一个素数p:

    [a^{p-1} = 1 | (a, p) = 1 ]

    对于G^W, 确定一个常数k,有W = k * (p-1) + M
    所以(G^W = G^{k*(p-1)} * G^M)
    显然G^W就等于G^M,所以问题就变成了求出组合数C(n,i)%(P-1)的值。
    计算组合数的时候,有两种方法:

    1. 暴力。因为组合数公式中有阶乘,所以用O(n)的时间暴力求
    2. 用空间换时间,预处理出fact(n).
      显然,如果直接算,我们需要预处理出1000000000这么多的fact,空间不允许。
      所以我们介绍lucas定理:

    [C(n, m, p) = C(frac{n}{p}, frac{m}{p}, p) * C(n \% p, m \% p, p) ]

    这样我们就有了高效的方法求组合数。
    但是lucas定理的应用有一个局限:p必须是素数。
    我们观察到:p-1并不是一个素数。
    这里介绍一个分解因数的软件:在linux下直接调用factor就可以分解因数,非常的方便,而且在NOI Linux中会预装此软件。
    分解因数,我们就可以应用中国剩余定理来合并线性方程。
    这里给出中国剩余定理:

    [egin{eqnarray} left{ egin{array}{lll} x = a_1 (mod b_1) \ x = a_2 (mod b_2)\ ......\ x = a_n (mod b_n) end{array} ight. end{eqnarray} ]

    那么,对于(prod(b_i))的剩余系

    [x = sum a_i * (frac{prod(b_i)}{b_i}) * inv(frac{prod(b_i)}{b_i}, b_i) ]

    其中,inv()为逆元,可以使用费马小定理求解(条件:P为素数)
    这样我们就解决了这个问题。
    这个问题有一个陷阱:
    当G == P时,(G, P) = 1, 费马小定理或者说欧拉定理在这里不适用,所以我们应该进行特判。
    另外,在数论程序编写时要特别注意模与乘法的问题。
    比如:x = a * b
    最好写成:
    (x = (ll) a * b % p) %= p
    比较保险。
    下面给出代码。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int P0=999911659;
    const int P1=999911658; 
    int G, N, M[4]; 
    int t[4] = {35617, 4679, 3, 2};
    int fac[66666];
    int pow(int a, int b, int p) { //calc a^b % p
        int ans = 1;
        for(int i = b; i; i >>= 1, a = (ll)a*a%p) {
            if(i & 1) ans = (ll)ans * a % p;
        }
        return ans;
    }
    /*int pow(int x, int y, int P) {
        int ret = 1;
        while(y) {
            if(y&1) ret = (ll)ret * x % P;
            x = (ll)x*x%P;
            y >>= 1;
        }
        return ret;
    }*/
    int inv(int x, int P){return pow(x, P-2, P);}
    int C(int n, int m, int P) { //calc (n, m) % t[x]
        if (n < m) return 0;
        return (ll)fac[n] * inv(fac[n-m], P) % P * inv(fac[m], P)%P;
    }
    int lucas(int n, int m, int P) {
        if(!n && !m) return 1;
        return (ll)lucas(n/P, m/P, P) * C(n%P, m%P, P) % P;
    }
    int main() {
        scanf("%d %d", &N, &G);
        if(G == P0) {
            puts("0");
            return 0;
        }
        for(int i = fac[0]=1; i <= t[0]; i++) fac[i] = (ll)fac[i-1]*i % P1;
        int tot = 0;
        for(int k = 0; k < 4; k++) {
            int now = 0;
            for(int i = 1; i * i <= N; i++) { if(N % i == 0) {
                (now += lucas(N, i, t[k])) %= t[k];
                if(N / i != i) {
                    (now += lucas(N, N/i, t[k])) %= t[k];
                }
            }}
            (tot += (ll)now * (P1/t[k]) % P1 * inv(P1/t[k], t[k]) % P1) %= P1;
        }
        printf("%d
    ", pow(G, tot, P0));
        return 0;
    }
    
  • 相关阅读:
    win32 tcp文件传输客户端
    Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析
    和菜鸟一起深入学习国嵌实验之vim常用命令
    和菜鸟一起深入学习国嵌实验之linux常用命令
    和菜鸟一起深入学习国嵌实验之文件编程
    和菜鸟一起深入学习国嵌实验之gcc分步编译&&gdb调试
    和菜鸟一起深入学习国嵌实验之简单Makefile
    获得创建表脚本
    oralce中获得创建脚本
    所有数据库连接字符串
  • 原文地址:https://www.cnblogs.com/gengchen/p/6347491.html
Copyright © 2011-2022 走看看