题目大意
给定 (n),每次询问一个 (m),求
[ask(m)=sum_{i=0}^{n}inom{3i}{m}
]
(nleq 10^6),询问次数 (leq 2 imes 10^5)
题解
[inom{3i}{m}=[x^m](1+x)^{3i}\
ask(m)=[x^m]sum_{i=0}^n(1+x)^{3i}
]
只要求出 (sum_{i=0}^n(1+x)^{3i}) 这个多项式,(m) 次项前的系数即为答案。
[sum_{i=0}^{infty}(1+x)^{3i}=sum_{i=0}^{infty}[(1+x)^3]^i=frac{1}{1-(1+x)^3}\
sum_{i=n+1}^{infty}(1+x)^{3i}=(1+x)^{3n+3}sum_{i=0}^{infty}(1+x)^{3i}=frac{(1+x)^{3n+3}}{1-(1+x)^3}\
sum_{i=0}^n(1+x)^{3i}=sum_{i=0}^{infty}(1+x)^{3i}-sum_{i=n+1}^{infty}(1+x)^{3i}=frac{1-(1+x)^{3n+3}}{1-(1+x)^3}=frac{(1+x)^{3n+3}-1}{3x+3x^2+x^3}
]
则有
[ask(m)=[x^m]frac{(1+x)^{3n+3}-1}{3x+3x^2+x^3}
]
分子可以组合数暴力展开,然后模拟大除法即可,时间复杂度 (O(n))。
Code
#include <bits/stdc++.h>
using namespace std;
#define LL long long
template<typename elemType>
inline void Read(elemType& T) {
elemType X = 0, w = 0; char ch = 0;
while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
T = (w ? -X : X);
}
const LL MOD = 1000000007LL;
const int maxn = 3000010;
LL inv[maxn], fact[maxn], finv[maxn];
void Init() {
inv[1] = fact[0] = fact[1] = finv[0] = finv[1] = 1;
for (int i = 2;i <= 3000005;++i) {
inv[i] = ((-(MOD / i) * inv[MOD % i]) % MOD + MOD) % MOD;
fact[i] = fact[i - 1] * i % MOD;
finv[i] = finv[i - 1] * inv[i] % MOD;
}
}
LL C(int n, int m) {
if (m > n) return 0;
return fact[n] * finv[m] % MOD * finv[n - m] % MOD;
}
LL f[maxn], h[maxn], g[10];
int n, q;
void Div(LL* f, int n, LL* g, LL m, LL* h) {
for (int i = 0;i <= n;++i) {
LL a = f[i] * inv[g[0]] % MOD;
h[i] = a;
for (int j = 0;j <= m;++j)
f[i + j] = ((f[i + j] - g[j] * a % MOD) % MOD + MOD) % MOD;
}
return;
}
void solve() {
for (int i = 0;i <= 3 * n + 3;++i)
f[i] = C(3 * n + 3, i);
f[0] = (f[0] - 1 + MOD) % MOD;
g[1] = 3; g[2] = 3; g[3] = 1;h[0] = 1;
Div(f + 1, 3 * n + 3 - 1, g + 1, 3 - 1, h + 1);
}
int main() {
Init();
Read(n);Read(q);
solve();
while (q--) {
int x;Read(x);
printf("%I64d
", h[x + 1]);
}
return 0;
}