bzoj 2655 calc —— 拉格朗日插值
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2655
先设 f[i][j] 表示长度为 i 的序列,范围是 1~j 的答案;
则 f[i][j] = f[i-1][j-1] * i * j + f[i][j-1],分别是选不选 j,选 j 的话放在哪个位置;
看不出次数...据说这是个最高次数为 2i 的多项式,感性理解...
知道了次数,就可以用拉格朗日插值算了,DP得到比较小的 2*n+1 个值,即可算出 x=A 的答案。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const xn=505; int n,A,mod,f[xn][xn<<1],yy[xn<<1]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return f?ret:-ret; } int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;} int pw(ll a,int b) { ll ret=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1)ret=(ret*a)%mod; return ret; } int main() { A=rd(); n=rd(); mod=rd(); int m=2*n+1; // f[0][0]=1; for(int j=0;j<=m;j++)f[0][j]=1;//!!! for(int i=1;i<=n;i++) for(int j=i;j<=m;j++) f[i][j]=((ll)f[i-1][j-1]*i%mod*j+f[i][j-1])%mod; if(A<=m){printf("%d\n",f[n][A]); return 0;} for(int i=1;i<=m;i++)yy[i]=f[n][i]; ll ans=0; for(int i=1;i<=m;i++) { ll s1=1,s2=1; for(int j=1;j<=m;j++) { if(i==j)continue; s1=(s1*(A-j)%mod+mod)%mod;// s2=(s2*(i-j)%mod+mod)%mod;// } ans=(ans+s1*pw(s2,mod-2)%mod*yy[i]%mod)%mod; } printf("%lld\n",ans); return 0; }