1. 程式人生 > >洛谷P5160 WD與迴圈

洛谷P5160 WD與迴圈

我們看這段程式碼

int cnt = 0;
for (int a_1 = 0; a_1 <= m; a_1++) {
    for (int a_2 = 0; a_1 + a_2 <= m; a_2++) {
    ...
        for (int a_n = 0; a_1 + a_2 + ... + a_n <= m; a_n++) {
            cnt = (cnt + 1) % 19491001;
        }
    }
}
printf("%d\n", cnt);

其實是可以改寫為

int cnt = 0;
for (int a_1 = 1; a_1 <= m + n; a_1++) {
    for (int a_2 = 1; a_1 + a_2 <= m + n; a_2++) {
    ...
        for (int a_n = 1; a_1 + a_2 + ... + a_n <= m + n; a_n++) {
            cnt = (cnt + 1) % 19491001;
        }
    }
}
printf("%d\n", cnt);

答案不變(就是把\(a_0, a_1, ... , a_n\)全部加了1,原始碼裡相應的\(m\)要增加\(n\),因為n個迴圈變數,每個變數都增加了1,所需增加即為\(n \times 1 = n\)

然後根據組合數學中組合數的定義,所求為C(m + n, n)

由於數特~別~大~,而且19491001是質數,所以這裡使用了Lucas定理

哦對了還要用乘法逆元的線性求法

下面程式碼

#include <bits/stdc++.h>
#define int long long
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")

using namespace std;

const int maxn = 20000000;
const int p = 19491001LL;
int n, inv[maxn], m, js[maxn];

int Lucas(int n, int m)
{
    if(n < m)return 0LL;
    if(n < p)return js[n] * inv[m] % p * inv[n - m] % p;
    return Lucas(n % p, m % p) * Lucas(n / p, m / p) % p;
}

signed main()
{
    int t;
    scanf("%lld", &t);
    js[0] = 1LL;
    for(register int i = 1LL; i <= p; i++)js[i] = js[i - 1] * i % p;
    inv[1] = 1LL; inv[0] = 1LL;
    for(register int i = 2LL; i <= p; i++)inv[i] = (p - p / i) * inv[p % i] % p;
    for(register int i = 2LL; i <= p; i++)inv[i] = inv[i] * inv[i - 1] % p;
    while(t--)
    {
        scanf("%lld%lld", &n, &m);
        printf("%lld\n", Lucas(n + m, m));
    }
    return 0;
}

三年OI一場空,不開long long見祖宗