Atcoder Beginner Contest 208 F - Cumulative Sum(拉格朗日插值)

简单题,随便写写吧。

问题等价于对所有从第 \(0\)​ 行上某个点到 \((n,m)\)​ 的路径,满足第一步是向上的,计算它们的 \(y^k\)​ 之和,其中 \(y\) 为起点纵坐标。因此首先考虑列出式子:

\[ans=\sum\limits_{i=1}^ni^k·\dbinom{n-i+m-1}{m-1} \]

由于此题 \(n,k\) 数据范围相对来说都比较大,因此直接求 / 斯特林拆幂貌似都不太可做,因此考虑从数据范围较小的 \(m\) 入手。注意到,下底数固定的组合数事实上也是多项式,即 \(\dbinom{n-i+m-1}{m-1}\) 是关于 \(i\) 的最高项为 \(x^{m-1}\) 的多项式,而由于 \(m\) 很小,该系数可以暴力 \(m^2\) 求。

于是问题转化为,求一个 \(m+k\) 次多项式在 \(n\) 处的前缀和。注意到,虽然这个 \(m+k\) 次多项式最高次数较高,但是系数非零的项不多,因此可以直接对这些项进行插值,复杂度 \(\Theta(m(m+k))\),当然也可以使用多项式 inv 一次性对所有 \(i\in[1,m+k]\)\(\sum\limits_{j=1}^nj^i\),时间复杂度 \(\Theta((m+k)\log (m+k))\),但对于此题来说没有必要,甚至因为模数是 \(10^9+7\) 而显得有些累赘。

const int MAXN = 2.5e6 + 33;
const int MOD = 1e9 + 7;
int qpow(int x, int e) {
	int ret = 1;
	for (; e; e >>= 1, x = 1ll * x * x % MOD)
		if (e & 1) ret = 1ll * ret * x % MOD;
	return ret;
}
ll n; int m, k;
int pw[MAXN + 5], sum[MAXN + 5], pre[MAXN + 5], suf[MAXN + 5];
int fac[MAXN + 5], ifac[MAXN + 5];
void init_fac(int n) {
	for (int i = (fac[0] = ifac[0] = ifac[1] = 1) + 1; i <= n; i++)
		ifac[i] = 1ll * ifac[MOD % i] * (MOD - MOD / i) % MOD;
	for (int i = 1; i <= n; i++) {
		fac[i] = 1ll * fac[i - 1] * i % MOD;
		ifac[i] = 1ll * ifac[i - 1] * ifac[i] % MOD;
	}
}
int pr[MAXN + 5], prcnt = 0, mnp[MAXN + 5], vis[MAXN + 5];
void sieve(int n) {
	for (int i = 2; i <= n; i++) {
		if (!vis[i]) pr[++prcnt] = i, mnp[i] = i;
		for (int j = 1; j <= prcnt && pr[j] * i <= n; j++) {
			vis[pr[j] * i] = 1; mnp[pr[j] * i] = pr[j];
			if (i % pr[j] == 0) break;
		}
	}
}
vector<int> conv(vector<int> a, vector<int> b) {
	vector<int> res(a.size() + b.size() - 1, 0);
	for (int i = 0; i < a.size(); i++) for (int j = 0; j < b.size(); j++)
		res[i + j] = (res[i + j] + 1ll * a[i] * b[j]) % MOD;
	return res;
}
int calc(ll n) {
	n %= MOD; int res = 0;
	for (int i = 1; i <= k + m + 2; i++) pw[i] = 1ll * pw[i] * i % MOD;
	for (int i = 1; i <= k + m + 2; i++) sum[i] = (sum[i - 1] + pw[i]) % MOD;
	pre[0] = suf[k + m + 3] = 1;
	for (int i = 1; i <= k + m + 2; i++) pre[i] = 1ll * pre[i - 1] * (n - i + MOD) % MOD;
	for (int i = k + m + 2; i; i--) suf[i] = 1ll * suf[i + 1] * (n - i + MOD) % MOD;
	for (int i = 1; i <= k + m + 2; i++) {
		int coef = 1ll * ifac[i - 1] * ifac[k + m + 2 - i] % MOD;
		coef = 1ll * coef * pre[i - 1] % MOD * suf[i + 1] % MOD;
		if ((k + m + 2 - i) & 1) coef = MOD - coef;
		res = (res + 1ll * coef * sum[i]) % MOD;
	}
	return res;
}
int main() {
	sieve(MAXN); init_fac(MAXN); scanf("%lld%d%d", &n, &m, &k);
	if (!m) return printf("%d\n", qpow(n % MOD, k)), 0;
	pw[1] = 1;
	for (int i = 2; i <= k + m + 2; i++) if (!vis[i]) pw[i] = qpow(i, k - 1);
	for (int i = 2; i <= k + m + 2; i++) if (vis[i]) pw[i] = 1ll * pw[mnp[i]] * pw[i / mnp[i]] % MOD;
	--m; vector<int> F; F.pb(ifac[m]); int ans = 0;
	for (int i = 0; i < m; i++) F = conv(F, vector<int>{m + n % MOD - i, MOD - 1});
//	for (int i = 0; i <= m; i++) printf("%d\n", 1ll * F[i] * 24 % MOD);
	for (int i = 0; i <= m; i++) ans = (ans + 1ll * calc(n) * F[i]) % MOD;
	printf("%d\n", ans);
	return 0;
}
posted @ 2022-01-03 22:33  tzc_wk  阅读(88)  评论(0)    收藏  举报