• 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;
    }
    
  • 相关阅读:
    正则表达式(十四)——找出某一个网页内部的所有的邮箱
    正则表达式(十三)——分组
    正则表达式(十二)——字符串的替换
    正则表达式(十一)——find和lookingAt
    查看隐藏文件夹
    SpringBoot 热部署
    oracle dmp文件泵导入
    python -爬虫-pycrul安装问题
    阿里云https tomcat配置
    jar包下载
  • 原文地址:https://www.cnblogs.com/ET2006/p/abc208F.html
Copyright © 2020-2023  润新知