1. 程式人生 > >四兩撥千斤,GCC編譯器(同餘模)

四兩撥千斤,GCC編譯器(同餘模)

對於兩個數M和N,如果M%P == N%P,則可以說M和N對P同餘。記作公式:

M≡N(modP)

同餘這一屬性看起來簡單,然而卻是數論中極為重要的概念。與之相關的公式和定理更是紛繁蕪雜,如果不是數學背景的童鞋,恐怕很難深入去鑽研所有的知識。

我們這一篇作為一個簡單的入門,用同餘公式來解決一個階乘問題。在做題之前,先來熟悉一個簡單的公式:

(M+N)%P=(M%P+N%P)%P(M+N)%P=(M%P+N%P)%P

這個公式簡單的說就是模運算的分配律,感性的可以認為,M對P的餘數X,以及N對P的餘數Y,兩者相加X+Y可能大於P,所以單獨取餘相加後要再取一次餘,保證結果不大於P。也正是該式子,能夠輕易解決下題中的一個階乘問題。

所謂取餘取餘,就是不斷地減去整的,留下餘的罷。

Problem Description

The GNU Compiler Collection (usually shortened to GCC) is a compiler system produced by the GNU Project supporting various programming languages. But it doesn’t contains the math operator “!”. In mathematics the symbol represents the factorial operation. The expression n! means "the product of the integers from 1 to n". For example, 4! (read four factorial) is 4 × 3 × 2 × 1 = 24. (0! is defined as 1, which is a neutral element in multiplication, not multiplied by anything.) We want you to help us with this formation: (0! + 1! + 2! + 3! + 4! + ... + n!)%m

gcc編譯器是GNU專案組的開發的一個編譯系統,支援多種程式語言。但是gcc不支援階乘符號!。對於表示式n!,它表示從1乘到n的一個值。比如4! = 4x3x2x1 = 24。0的階乘為1,表示不乘以任何值。

Input

The first line consists of an integer T, indicating the number of test cases. Each test on a single consists of two integer n and m.

第一行表示測試用例個數T

後面每行包含2個整數n和m

Output

Output the answer of (0! + 1! + 2! + 3! + 4! + ... + n!)%m. 輸出 (0! + 1! + 2! + 3! + 4! + ... + n!)%m.

Constrains 0 < T <= 20 0 <= n < 10^100 (without leading zero) 0 < m < 1000000

Sample Input

1 
10 861017

Sample Output

593846

這道題就是上面公式的典型應用,對於一個階乘n!來說,如果m<n,則必有:n! % m = 0

也就是說,n!對所有小於等於n的數取模都是0。

數學最大的特點之一就是喜歡說廢話,而且還要證明。

通過取餘避免了大數運算,另外本題精巧之處還在於在一次for迴圈中計算了從階乘1!、2!一直到(m-1)!的和。

本文隸屬於演算法合集中的一篇,更多內容可以通過選單欄點選演算法合集檢視~

原始碼:GCC

#include <stdio.h>
#include <stdint.h>

int main()
{
    int count;
    scanf("%d", &count);

    int64_t ans;
    int64_t futher, num, mod;

    char nstr[200];

    while (count--)
    {
        scanf("%s", nstr);
        scanf("%I64d", &mod);

        //公式: (a+b)%c=(a%c+b%c)%c
        //可以知道 m!+(m+1)!….n!這一些對m取餘都是0,所以就不需考慮了。
        //所以判斷n和m的大小,取較小值。
        if (strlen(nstr) >= 7)
            num = 1000000;
        else
            sscanf(nstr, "%I64d", &num);

        if (num > mod)
            num = mod;

        futher = 1;
        ans = 1;

        for (int i = 1; i <= num; i++)
        {
            futher = (futher * i) % mod;
            //如果能夠整除mod,說明後面的數都可以整除mod,不需要再計算
            if (futher == 0)
                break;
            //每次將模加入到ans中
            ans += futher;
        }

        printf("%I64d\n", ans % mod);
    }
    return 0;
}

個人公眾號:ACM演算法日常

專注於基礎演算法的研究工作,深入解析ACM演算法題,五分鐘閱讀,輕鬆理解每一行原始碼。內容涉及演算法、C/C++、機器學習等。