【bzoj3157】【bzoj3516】【bzoj4126】国王奇遇记 线性插值法

       题解可以戳这个,然后建议去看一下杜教ppt

       大概就是已知若干个点值,然后可以用组合的性质用自己把自己带进去然后O(M)得到任意一个点值把。(M为多项式的次数),组合数还是很神奇的。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 500005
#define mod 1000000007
using namespace std;

int n,m,cnt,a[N],b[N],c[N],u[N],v[N],pw[N],fac[N],inv[N],p[N];
int ksm(int x,int y){
	int t=1; for (; y; y>>=1,x=(ll)x*x%mod) if (y&1) t=(ll)t*x%mod;
	return t;
}
void pfs(){
	int i,j; pw[1]=1;
	for (i=2; i<=m+1; i++){
		if (!pw[i]){ pw[i]=ksm(i,m); c[++cnt]=i; }
		for (j=1; j<=cnt && i*c[j]<=m+1; j++){
			pw[i*c[j]]=(ll)pw[i]*pw[c[j]]%mod;
			if (!(i%c[j])) break;
		}
	}
}
int cbn(int x,int y){
	return (ll)fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main(){
	scanf("%d%d",&n,&m); int i;
	if (m==1){ printf("%d\n",((ll)n*(n+1)>>1)%mod); return 0; }
	pfs();
	if (n<=m){
		int ans=0,base=m;
		for (i=1; i<=n; i++,base=(ll)base*m%mod) ans=((ll)base*pw[i]+ans)%mod;
		printf("%d\n",ans); return 0;
	}
	inv[0]=inv[1]=fac[0]=fac[1]=1;
	for (i=2; i<=m+1; i++){
		inv[i]=mod-(ll)inv[mod%i]*(mod/i)%mod;
		fac[i]=(ll)fac[i-1]*i%mod;
	}
	a[0]=1; b[0]=0;
	for (i=1; i<=m+1; i++){
		a[i]=(ll)a[i-1]*inv[m]%mod; b[i]=((ll)b[i-1]*inv[m]+pw[i])%mod;
	}
	for (i=1; i<=m+1; i++) inv[i]=(ll)inv[i-1]*inv[i]%mod;
	int t1=0,t2=0;
	for (i=0; i<=m+1; i++){
		int tmp=cbn(m+1,i); if (i&1) tmp=mod-tmp;
		t1=((ll)tmp*a[i]+t1)%mod; t2=((ll)tmp*b[i]+t2)%mod;
	}
	p[0]=(ll)(mod-t2)*ksm(t1,mod-2)%mod;
	for (i=1; i<=m; i++) p[i]=((ll)p[0]*a[i]+b[i])%mod;
	u[0]=1; v[m]=1; int ans=0;
	for (i=1; i<=m; i++) u[i]=(ll)u[i-1]*(n-i+1)%mod;
	for (i=m-1; i>=0; i--) v[i]=(ll)v[i+1]*(n-i-1)%mod;
	for (i=0; i<=m; i++){
		int tmp=(ll)u[i]*v[i]%mod*inv[i]%mod*inv[m-i]%mod;
		if ((m^i)&1) tmp=mod-tmp;
		ans=((ll)p[i]*tmp+ans)%mod;
	}
	printf("%d\n",((ll)ans*ksm(m,n)-p[0]+mod)%mod);
	return 0;
}


by lych

2016.3.17

你可能感兴趣的:(组合数学,快速幂,多项式,逆元,线性插值法)