bzoj 2655 calc——拉格朗日插值

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2655

先考虑DP。dp[ i ][ j ]表示值域为 i 、选 j 个值的答案,则 dp[ i ][ j ] = dp[ i-1 ][ j ] + dp[ i-1 ][ j-1] * i * j 。两项分别表示一定不选/一定选第 i 个值。

因为答案是值域大、个数小,所以考虑只看 dp[ ][ n ] ,即把值域看成自变量。

不知怎么知道这个式子的次数是 2*n 。尝试用做几遍差分看什么时候数列都为0的方法来看,但得出应该是 2*n - 2 次才对呀……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=4,K=12,M=505;
ll dp[M][M],c[M];
int pw(int x,int k)
{
  int ret=1;while(k){if(k&1)ret*=x;x*=x;k>>=1;}return ret;
}
int main()
{
  dp[0][0]=1;
  for(int i=1;i<=K;i++)
    for(int j=1;j<=N;j++)
      dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*i*j;
  for(int i=N;i<=K;i++)c[i]=dp[i][N];
  int cnt=0,nw=N;
  while(c[K])
    {
      for(int i=K;i>=nw;i--)
    c[i]-=c[i-1]; c[nw-1]=0;
      for(int i=1;i<=K;i++)
    printf("%6lld ",c[i]); puts("");
      nw++; cnt++;
    }
  printf("cnt=%d\n",cnt);
  return 0;
}
打表观察

以为值域<个数的dp无意义,于是选择 n~3*n 这 2*n+1 个值。但其实值域<个数的也能用。

注意 x[ i ] - x[ j ] 有负数,最后(答案+mod)%mod。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=505;
int n,A,mod,dp[N*3][N],ans;
int pw(int x,int k)
{
  int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;
}
int main()
{
  scanf("%d%d%d",&A,&n,&mod);
  int lm=n*3;
  for(int i=0;i<=lm;i++)dp[i][0]=1;///
  for(int i=1;i<=lm;i++)
    for(int j=1;j<=i&&j<=n;j++)
      dp[i][j]=(dp[i-1][j]+(ll)dp[i-1][j-1]*i%mod*j)%mod;
  if(A<=lm)
    {
      printf("%d\n",dp[A][n]);return 0;
    }
  int s0,s1;
  for(int i=n;i<=lm;i++)
    {
      s0=1; s1=1;//////
      for(int j=n;j<=lm;j++)
    {
      if(j==i)continue;
      s0=(ll)s0*(A-j)%mod;  s1=(ll)s1*(i-j)%mod;
    }
      ans=(ans+(ll)s0*pw(s1,mod-2)%mod*dp[i][n]%mod)%mod;
    }
  printf("%d\n",(ans+mod)%mod);
  return 0;
}

 

posted on 2018-11-23 20:46  Narh  阅读(129)  评论(0编辑  收藏  举报

导航