洛谷 P4351 [CERC2015]Frightful Formula 題解
一、題目:
二、思路:
一道數學題。
先不管公式中的常數項 \(c\),首先考慮第一行和第一列的貢獻。
對於位於 \((i,1)\) 的數 \(x\),它的貢獻為:\(\dbinom{2n - i-2}{n-i}\times a^{n-1}b^{n-i}x\)。
對於位於 \((1,j)\) 的數 \(x\),它的貢獻為:\(\dbinom{2n-j-2}{n-j}\times a^{n-j}b^{n-1}x\)。
再來考慮常數項 \(c\) 的影響。我們可以發現,加入在位置 \((i,j)\) 上加了一個常數 \(c\),那麼這個 \(c\) 最終就會“演變成”\(\dbinom{2n-i-j}{n-i}a^{n-j}b^{n-i}c\)
關於為什麼是這樣的,我們可以這麼考慮:假設一個人從 \((i,j)\) 要走到 \((n,n)\),每向右走都要乘 \(a\),向下走都要乘 \(b\),那麼乘 \(a\) 的次數和乘 \(b\) 的次數都是確定的。而又因為一共有 \(\dbinom{2n-i-j}{n-i}\) 種走法,所以最終的總貢獻就是上面那個式子啦!
又因為 \(\forall 2\leq i,j\leq n\),\((i,j)\) 都要加上常數 \(c\)。所以它們的總貢獻為 \(c\sum\limits_{i=2}^n\sum\limits_{j=2}^n\dbinom{2n-i-j}{n-i}b^{n-i}a^{n-j}\)
考慮這個式子怎麼進行化簡。設 \(k=2n-i-j\),更改列舉順序,有:
\[\begin{aligned} \text{Original Formula} &= c\sum\limits_{k=0}^{2n-4}\left(\sum\limits_{i=\max\{0,k-n+2\}}^{\min\{k,n-2\}}\dbinom{k}{i}b^{i}a^{k-i} \right) \end{aligned} \]將和式分為兩部分。
第一部分:
\[\begin{aligned} \text{The First Part} &= c\sum\limits_{k=0}^{n-2}\sum\limits_{i=0}^k\dbinom{k}{i} b^i a^{k-i}\\ &= c\sum\limits_{k=0}^{n-2}(a+b)^k \end{aligned} \]第二部分:
其中 $f_k=\sum\limits_{i=k-(n-2)}^{n-2} \dbinom{k}{i}b^i a^{k-i} $。則根據組合數的遞推公式,可以得到
\[f_k=(a+b)f_{k-1}-\dbinom{k-1}{k-n+1}b^{k-n+1}a^{n-1}-\dbinom{k-1}{n-2}b^{n-1}a^{k-n+1} \]總時間複雜度:\(O(n)\)。
三、程式碼:
#include <iostream> // 目前是洛谷rk1
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MAXN = 200005, MOD = 1000003;
int n, a, c, b;
long long factor[MAXN << 1], facinv[MAXN << 1], ans;
long long powa[MAXN], powb[MAXN], f[MAXN << 1];
inline long long power(long long a, long long b) {
long long res = 1;
for (; b; b >>= 1) {
if (b & 1) res = 1LL * res * a % MOD;
a = 1LL * a * a % MOD;
}
return res;
}
inline void prework(void) {
factor[0] = 1;
for (int i = 1; i <= 2 * n; ++ i) factor[i] = factor[i - 1] * i % MOD;
facinv[2 * n] = power(factor[2 * n], MOD - 2);
for (int i = 2 * n - 1; i >= 0; -- i) facinv[i] = facinv[i + 1] * (i + 1) % MOD;
powa[0] = 1; powb[0] = 1;
for (int i = 1; i <= n; ++ i)
powa[i] = powa[i - 1] * a % MOD,
powb[i] = powb[i - 1] * b % MOD;
}
inline long long C(int n, int m) {
if (n < m) return 0;
return factor[n] * facinv[m] % MOD * facinv[n - m] % MOD;
}
inline long long get_f(int k) {
long long res = 0;
for (int i = k - (n - 2); i <= n - 2; ++ i)
(res += C(k, i) * powb[i] % MOD * powa[k - i] % MOD) %= MOD;
return res;
}
int main() {
n = read(); a = read(); b = read(); c = read();
prework();
for (int i = 1; i <= n; ++ i) {
long long x = read();
if (i == 1) continue;
long long tmp = C(2 * n - i - 2, n - i) * powa[n - 1] % MOD * powb[n - i] % MOD * x;
(ans += tmp) %= MOD;
}
for (int j = 1; j <= n; ++ j) {
long long x = read();
if (j == 1) continue;
long long tmp = C(2 * n - j - 2, n - j) * powa[n - j] % MOD * powb[n - 1] % MOD * x;
(ans += tmp) %= MOD;
}
{
long long tmp = 1;
for (int k = 0; k <= n - 2; ++ k) {
if (k != 0) tmp = tmp * (a + b) % MOD;
(ans += c * tmp % MOD) %= MOD;
}
}
f[n - 1] = get_f(n - 1);
for (int k = n - 1; k <= 2 * n - 4; ++ k) {
if (k != n - 1) {
f[k] = (a + b) * f[k - 1] % MOD
- C(k - 1, k - n + 1) * powb[k - n + 1] % MOD * powa[n - 1] % MOD
- C(k - 1, n - 2) * powb[n - 1] % MOD * powa[k - n + 1] % MOD;
f[k] %= MOD;
if (f[k] < 0) f[k] += MOD;
}
(ans += c * f[k] % MOD) %= MOD;
}
printf("%lld\n", ans);
return 0;
}