얼핏 보면 단순한 동적계획법 문제이지만 무식하게 풀면 시간복잡도가 $O(n^2)$으로 절대 제한 시간 내에 풀 수 없습니다. 따라서 $F[n,n]$을 $l_k$와 $t_k$로 나타내는 식을 구하고, 그걸 최대한 빠르게 계산하는 방향으로 풀어야 합니다.
그러나 이 $F[n,n]$의 식을 구하는 게 매우 어렵습니다. 생각의 전환이 필요한데, 각 $l_k$, $t_k$가 $F[n,n]$에 기여하는 정도를 계산합시다. 여기서 기여하는 정도라는 건, 어떤 칸 $F[x,y]$의 값을 다른 칸 $F[i,j]$를 포함하는 수식으로 나타냈을 때 $F[i,j]$의 계수가 $F[i,j]$가 $F[x,y]$에 기여하는 정도입니다. 예를 들어, $F[i,j]$는 $F[i,j+1]$에 $a$만큼 기여하고, $F[i,j]$는 $F[i+1,j]$에 $b$만큼 기여합니다. 마찬가지로 $F[i,j+2]$에는 $a^2$만큼, $F[i+1,j+1]$에는 $2ab$만큼, $F[i+2,j]$에는 $b^2$만큼 기여합니다. 즉, $F[i,j]$의 기여도는 오른쪽 아래로 내려갈수록 점화식에 의해 증폭되고, 증폭 정도는 오른쪽으로 한 칸 갈 때 $a$배, 아래로 한 칸 갈 때 $b$배입니다. 따라서 $F[i,j]$가 $F[x,y]$에 기여하는 정도는
- $F[i,j]$는 $F[i,j]$에 1만큼 기여
- 이 기여도 $(i,j)$에서 $(x,y)$까지 가면서 $a^{x-i}b^{y-j}$배 증폭
- 그런데 $(i,j)$에서 $(x,y)$까지 갈 수 있는 경로는 $\binom{x+y-i-j}{x-i}$개 있으므로 이를 곱해주어야 함
예를 들어, $F[i,j]$가 $F[i+1,j+1]$에 기여하는 정도를 구한다고 하면 $F[i,j]$는 $F[i,j]$에 1만큼 기여하고, 이 기여도가 오른쪽으로 한 칸, 아래로 한 칸 이동하면서 $ab$배로 증폭됩니다. 그런데 $(i,j)$에서 $(i+1,j+1)$까지 가는 경로는 두 개 있으므로 증폭이 두 번 일어납니다. 따라서 최종 기여도는 $2ab$입니다.
결과적으로 $l_k$는 $F[k,2]$에 $a$만큼 기여하고, $(n,n)$까지 가면서 기여도는 $(2n-k-2n-2)a^{n-2}b^{n-k}$배로 증폭되므로 $l_k$가 $F[n,n]$에 기여하는 정도는
가 됩니다. 같은 방법으로 $t_k$는 $F[n,n]$에 $\binom{2n-k-2}{n-2}a^{n-k}b^{n-1}$만큼 기여합니다. 이 기여도를 다 더하면
위 식은 $l_k$와 $t_k$만 고려한 것이고, 실제로는 $c$항이 더해져야 합니다. 이 $c$도 역시 $(n,n)$까지 가면서 똑같이 증폭됩니다. 그러므로 이를 고려한 전체 식은
새로 추가된 항은 아래와 같이 단순화할 수 있습니다.
$f(n) = \sum_{i=0}^n \sum_{j=0}^n \binom{i+j}{i} a^j b^i$로 정의하면 저 값은 $cf(n-2)$입니다. 그리고
또 $g_a(n)=\sum_{i=0}^n\binom{n+i}{n}a^i$로 정의하면
여기서 $a$가 1일 때와 1이 아닐 때로 나뉩니다. 1이 아니면
반대로 1이면
마찬가지로 $a$ 대신 $b$를 써서 $g_b(n)$을 정의하고 그 점화식을 구할 수 있습니다. 정리하면 다음과 같습니다.
$g_a(n)$, $g_b(n)$, $f(n)$은 모두 $O(n)$에 계산 가능하고, 따라서 $cf(n-2)$도 $O(n)$에 계산할 수 있습니다.
#include<bits/stdc++.h>
using namespace std;
long long n, a, b, c;
long long l[200001], t[200001];
long long powa[200001] = {1}, powb[200001] = {1}; // powa[i] = a^i, powb[i] = b^i
long long fac[400001] = {1}, facinv[400001]; // fac[i] = i!, facinv[i] = 1/i!
long long f[200001] = {1}, ga[200001] = {1}, gb[200001] = {1};
const long long MOD = 1000003;
// calculate x^y % MOD
inline long long powmod(long long x, long long y) {
if (y == 0)
return 1;
long long half = powmod(x, y/2);
if (y % 2 == 1)
return half * half % MOD * x % MOD;
return half * half % MOD;
}
// calculate binomial coefficient Comb(x, y)
inline long long binom(long long x, long long y) {
return fac[x] * facinv[y] % MOD * facinv[x-y] % MOD;
}
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n >> a >> b >> c;
for (int i = 1; i <= n; i++)
cin >> l[i];
for (int i = 1; i <= n; i++)
cin >> t[i];
for (int i = 1; i <= n; i++) {
powa[i] = powa[i-1] * a % MOD;
powb[i] = powb[i-1] * b % MOD;
}
for (int i = 1; i <= 2*n; i++)
fac[i] = fac[i-1] * i % MOD;
facinv[2*n] = powmod(fac[2*n], MOD-2);
for (int i = 2*n-1; i >= 0; i--)
facinv[i] = facinv[i+1] * (i+1) % MOD;
long long inv1a = powmod(1-a+MOD, MOD-2), inv1b = powmod(1-b+MOD, MOD-2);
for (int i = 1; i <= n-2; i++) {
ga[i] = (a == 1? binom(2*i+1, i) : inv1a * (ga[i-1] + binom(2*i-1, i)*powa[i] - binom(2*i, i)*powa[i+1] + MOD*MOD) % MOD);
gb[i] = (b == 1? binom(2*i+1, i) : inv1b * (gb[i-1] + binom(2*i-1, i)*powb[i] - binom(2*i, i)*powb[i+1] + MOD*MOD) % MOD);
f[i] = (f[i-1] + powa[i]*gb[i] + powb[i]*ga[i] - binom(2*i, i)*powa[i]%MOD*powb[i] + MOD*MOD) % MOD;
}
long long res = 0;
for (int i = 2; i <= n; i++)
res = (res + (l[i] * binom(2*n-i-2, n-2) % MOD) * (powa[n-1] * powb[n-i] % MOD) % MOD) % MOD;
for (int i = 2; i <= n; i++)
res = (res + (t[i] * binom(2*n-i-2, n-2) % MOD) * (powa[n-i] * powb[n-1] % MOD) % MOD) % MOD;
res = (res + c*f[n-2]) % MOD;
cout << res;
return 0;
}
문제에서 주어진 모듈러가 106+3으로 소수이기 때문에 잉여역수를 쉽게 구할 수 있습니다.