题解 CF622F 【The Sum of the k-th Powers】

题目链接

Solution CF622F The Sum of the k-th Powers

题目大意:给定\(i,k\),求\(\sum_{i=1}^ni^k\)

拉格朗日插值


分析:首先类似于积分,自然数\(k\)次幂的前缀和\(F(n)\)可以用一个\(k + 1\)次的多项式表达出来

那么我们就可以选取\(k + 2\)个点,用拉格朗日插值求出\(F(n)\)

注意,如果随便选择点的话时间复杂度是\(O(k^2)\)的,我们通过选取连续的点可以做到\(O(k)\)

\(F(n)=\sum_{i=1}^{k+2}(i^k \cdot \frac{\prod_{j \neq i}n-j}{\prod_{j \neq i}i-j})\)

首先观察分子,我们可以通过讨论\(n\)\(k+2\)的大小关系,如果\(n \leq k + 2\)那么我们就暴力算,否则插值,这样可以避免分子的符号讨论

对于分子,我们只需要预处理出\(n-i \quad i \in [1,k+2]\)的前、后缀积就可以\(O(1)\)求得

对于分母,观察发现实质上是两个阶乘之积,只不过要根据\(k+2-i\)的奇偶性来讨论符号

\(i^k\)可以快速幂在\(O(logk)\)的时间内求得

总复杂度\(O(klogk)\)

#include <cstdio>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7,maxk = 1e6 + 100;
typedef long long ll;
ll ans,fac[maxk],suf[maxk],pre[maxk],sum;
int n,k;
ll add(ll a,ll b){return (a + b) % mod;}
ll mul(ll a,ll b){return (a * b) % mod;}
ll qpow(ll a,ll b){
	ll res = 1,base = a;
	while(b){
		if(b & 1)res = mul(res,base);
		base = mul(base,base);
		b >>= 1;
	}
	return res;
}
ll inv(ll x){return qpow(x,mod - 2);}
ll pro(ll a,ll b){
	return mul(fac[b],inv(fac[a - 1]));
}
inline void init(){
	fac[0] = pre[0] = suf[k + 3] = 1;
	for(int i = 1;i <= k + 2;i++)fac[i] = mul(fac[i - 1],i);
	for(int i = 1;i <= k + 2;i++)pre[i] = mul(pre[i - 1],n - i);
	for(int i = k + 2;i >= 1;i--)suf[i] = mul(suf[i + 1],n - i);
}
int main(){
	scanf("%d %d",&n,&k);
	if(n <= k + 2){
		for(int i = 1;i <= n;i++)ans = add(ans,qpow(i,k));
		printf("%lld\n",ans);
		return 0;
	}
	init();
	for(int i = 1;i <= k + 2;i++){
		ll s1 = 1,s2 = 1;
		if(i != 1)s1 = mul(s1,pre[i - 1]);
		if(i != k + 2)s1 = mul(s1,suf[i + 1]);

		if(i != 1)s2 = mul(s2,pro(1,i - 1));
		if(i != k + 2)s2 = mul(s2,((k + 2 - i) % 2) ? (mod - pro(1,k + 2 - i)) : pro(1,k + 2 - i));

		sum = add(sum,qpow(i,k));
		ans = add(ans,mul(sum,mul(s1,inv(s2))));
	}
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/colazcy/p/13380344.html