[拉格朗日插值法][dp] Luogu P4463 calc

 

题目描述

一个序列a_1,\cdots,a_na1,,an是合法的,当且仅当:

长度为给定的nn。

a_1,\cdots,a_na1,,an都是[1,A][1,A]中的整数。

a_1,\cdots,a_na1,,an互不相等。

一个序列的值定义为它里面所有数的乘积,即a_1\times a_2\times\cdots\times a_na1×a2××an

求所有不同合法序列的值的和。

两个序列不同当且仅当他们任意一位不一样。

输出答案对一个数modmod取余的结果。

 

题解

  • 我们可以先统计出数值递增的贡献和,为贡献和*n!
  • 设f[i][j]表示前i个数选的值都小于等于j的贡献和,转移为f[i][j]=f[i-1][j-]*j+f[i][j-1]
  • 然后呢我们就可以暴力做拉格朗日插值法就好了

 

代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #define ll long long 
 5 int a,n,mo,res,m,f[510][1010],y[1010];
 6 using namespace std;
 7 int ksm(int a,int b) { int r=1; for (;b;b>>=1,a=1ll*a*a%mo) if (b&1) r=1ll*r*a%mo; return r; }
 8 int change(int x)
 9 {
10     if (x>=1&&x<=m) return y[x];
11     int r=0;
12     for (int i=1;i<=m;i++)
13     {
14         int p=y[i],q=1;
15         for (int j=1;j<=m;j++) if (i!=j) p=1ll*p*(x-j)%mo,q=1ll*q*(i-j)%mo;
16         p<0?p+=mo:0,q<0?q+=mo:0,r=(r+1ll*p*ksm(q,mo-2))%mo;
17     }
18     return r;
19 }
20 int main()
21 {
22     scanf("%d%d%d",&a,&n,&mo),res=1,m=2*n+1;
23     for (int i=1;i<=n;i++) res=1ll*res*i%mo;
24     for (int i=0;i<=m;i++) f[0][i]=1;
25     for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) f[i][j]=(1ll*f[i-1][j-1]*j+f[i][j-1])%mo;
26     for (int i=1;i<=m;i++) y[i]=f[n][i];
27     printf("%lld",1ll*res*change(a)%mo);     
28 }

 

posted @ 2019-07-30 12:42  BEYang_Z  阅读(153)  评论(0编辑  收藏  举报