1. 程式人生 > >【CF622F】The Sum of the k-th Powers-拉格朗日插值

【CF622F】The Sum of the k-th Powers-拉格朗日插值

測試地址:The Sum of the k-th Powers 題目大意:i=1nik\sum_{i=1}^ni^k109+710^9+7取模的值。 做法: 本題需要用到拉格朗日插值。 容易看出(或者用數學歸納法簡單證明),答案f(n)f(n)是一個關於nn的最高次為k+1k+1的多項式。問題是,怎麼得到這個多項式呢?這時候就要使用拉格朗日插值法。 拉格朗日插值法是一個可以由k+1k+1個二維平面上的點(xi,yi)(x_i,y_i),構造出一個正好穿過這些點的kk次函式f(x)f(x)的演算法。有: f(x)=i

=1k+1yili(x)f(x)=\sum_{i=1}^{k+1}y_i\cdot l_i(x) 其中li(x)l_i(x)稱為插值基函式,其表示式為ijxxjxixj\prod_{i\ne j}\frac{x-x_j}{x_i-x_j}。 為什麼有這個式子呢?首先看存在性,注意到li(x)l_i(x)當且僅當x=xix=x_i時有取值11,否則當x=xj(ij)x=x_j(i\ne j)時取值為00,那麼顯然上面的函式可以穿過對應的點。至於唯一性好像要用一些玄學的東西證,你只需要知道k
+1k+1
個點一定能確定一個kk次函式就行了… 回到這一題,我們顯然可以用(i,j=1ijk)(0ik+1)(i,\sum_{j=1}^ij^k)(0\le i\le k+1)這些點來使用拉格朗日插值法,算出這些點顯然是O(klogk)O(k\log k)的(或者你可以用線性篩優化到O(k)O(k)…)。接下來我們把nn代入上式: f(n)=i=0k1yiijnjijf(n)=\sum_{i=0}^{k-1}y_i\cdot \prod_{i\ne j}\frac{n-j}{i-j} 後面的積式中,分子和分母可以分別預處理,分子只要處理出字首積和字尾積即可(不能用1ni(nj)\frac{1}{n-i}\cdot \prod (n-j)這個式子,因為nin-i有可能為00),而對於分母,我們發現分母可以拆成兩個類似於下面這樣的部分:i=1x1i\prod_{i=1}^x\frac{1}{i}i=1x1(i)\prod_{i=1}^x\frac{1}{(-i)},分別預處理出來即可,時間複雜度為O(klogk)O(k\log k)(或者用線性求逆元優化成O(k)O(k))。 所以我們就解決了此題,時間複雜度為O(klogk)O(k\log k)(可以優化到O(k)O(k))。 以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll n,k,invp[1000010],invn[1000010],inv[1000010];
ll pre[1000010],suf[1000010];

ll power(ll a,ll b)
{
	ll s=1,ss=a;
	while(b)
	{
		if (b&1) s=s*ss%mod;
		ss=ss*ss%mod;
		b>>=1;
	}
	return s;
}

int main()
{
	scanf("%lld%lld",&n,&k);
	invp[0]=invn[0]=1;
	pre[0]=n;
	for(ll i=1;i<=k+1;i++)
	{
		invp[i]=invp[i-1]*power(i,mod-2)%mod;
		invn[i]=invn[i-1]*power(mod-i,mod-2)%mod;
		pre[i]=pre[i-1]*(n-i+mod)%mod;
	}
	suf[k+1]=(n-k-1+mod)%mod;
	for(int i=k;i>=0;i--)
		suf[i]=suf[i+1]*(n-i+mod)%mod;
	
	ll ans=0,now=0;
	for(int i=0;i<=k+1;i++)
	{
		if (k>0||i>0) now=(now+power(i,k))%mod;
		ll tot=1;
		if (i>0) tot=tot*pre[i-1]%mod;
		if (i<k+1) tot=tot*suf[i+1]%mod;
		ll nowtot=tot*invp[i]%mod*invn[k-i+1]%mod;
		ans=(ans+now*nowtot)%mod;
	}
	printf("%lld",ans);
	
	return 0;
}