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;
}

 

posted @ 2018-11-23 16:00  Zinn  阅读(148)  评论(0编辑  收藏  举报