cf#ECR7 - F - The Sum of the k-th Powers -拉格朗日插值/逆元/数学


 http://codeforces.com/contest/622/problem/F
F. The Sum of the k-th Powers
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

There are well-known formulas: . Also mathematicians found similar formulas for higher degrees.

Find the value of the sum  modulo 109 + 7 (so you should find the remainder after dividing the answer by the value 109 + 7).

Input

The only line contains two integers n, k (1 ≤ n ≤ 109, 0 ≤ k ≤ 106).

Output

Print the only integer a — the remainder after dividing the value of the sum by the value 109 + 7.

Sample test(s)
input
4 1
output
10
input
4 2
output
30
input
4 3
output
100
input
4 0
output
4


题意很明显是求 sum= 1^k+ 2^k + 3^k+......n^k

n《1e9,k《1e6

直接求显然不太行得通啦,根据题目的提示,对于 i^k 的sum,总存在一个 最高次为k+1的通项,我们要找到这样一个通项才能解决问题。

而看到这个K 是1e6   根据拉格朗日插值公式,我们可以用x个点,来构造出一个最高次为x-1的通项公式,

根据题目,我们要找到一个最高次为k+1次的通项,因此,我们只需要求出k+2个点即可求出通项

唯一性:我们确定了多项式的最高次为k+1,通过范德蒙行列式 和 克莱姆法则,可以判定如果这n+2个点的x值(每个点为(x,sum(x)))各不相同,那么这个多项式是唯一的....可以自己翻翻怎么证。。孱弱就不懂了。。

下面看怎么求这个通项。

关于拉格朗日插值公式的使用可以看看这个文章:

http://www.guokr.com/post/456777/ 

根据公式,我们的通项f(n)= y[1]* (n-2) (n-3)...(n-k-2) / 【(1-2)(1-3)....(1-(k+2))】  ....以此类推k+2项

令tol= (n-1) (n-2) (n-3)...(n-(k+2))

也就是  每一项 A[i] =  y[i] * tol/(n-i)  / 【一个带正负号的阶乘】

由于tol 非常大,计算过程要取模,还有除法,要用到逆元公式,后面的第二个除法也要用到

注意判断符号的正负,然后就没有什么问题啦。

写得太挫,跑了1.5S

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std; 
__int64 mod=1000000000+7;
__int64 pow_m(__int64 a,__int64 b)
{
	__int64 ans=1;
	__int64 tmp=a;
	while(b)
	{
		if (b&1)
			ans=ans*tmp%mod;
		tmp=tmp*tmp%mod;
		b>>=1;
	}
	return ans;
}
__int64 y[1000000+60];		//预处理 前k+2的sum
__int64 jie[1000000+60];	//阶乘预处理
int n,k;
__int64 get_jie(int x)		//得到x位的分母
{
	if (x==1||x==k+2) return jie[k+1];
	else
		return jie[x-1]*jie[k+2-x]%mod;
}
int main()
{
	
	cin>>n>>k;
	int i;
	for (i=1;i<=k+2;i++)			//预处理sum_of  i^k
		y[i]=(y[i-1]+pow_m(i,k))%mod; 
	
	if (n<=k+2)
	{	
		printf("%I64d\n",y[n]); return 0;
	}
	jie[0]=1;
	for (i=1;i<=k+2;i++)		//预处理阶乘
		jie[i]=jie[i-1]*i%mod ;
	
	__int64 tol=1;
	for (i=1;i<=k+2;i++)		//分子部分的总乘积	
		tol=tol*(n-i)%mod;
	__int64 ans=0;
	__int64 tmp;	
	for (i=1;i<=k+2;i++)		//根据拉格朗日插值,有k+2项 
	{
		tmp=  tol*pow_m(n-i,mod-2)%mod ;			//分子= 总乘积tol/(n-i)
		tmp= pow_m(get_jie(i),mod-2)%mod*tmp%mod;	//分母为get_jie(i)部分
		int one;
		if ((k+2-i)%2)		//判断正负
			one=-1;
		else 
			one=1; 
		ans= (  ans+ one*tmp*y[i]   )%mod;		//记得乘上Y【i】  
	}
	
	while(ans<0) 
		ans+=mod; 
	printf("%I64d\n",ans%mod); 
	
	
	
	return 0;
	
} 





 



你可能感兴趣的:(CF,数学,费马小定理,数学,拉格朗日插值)