2655: calc
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 182 Solved: 119
[Submit][Status][Discuss]
Description
一个序列a1,...,an是合法的,当且仅当:
长度为给定的n。
a1,...,an都是[1,A]中的整数。
a1,...,an互不相等。
一个序列的值定义为它里面所有数的乘积,即a1a2...an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数mod取余的结果。
Input
一行3个数,A,n,mod。意义为上面所说的。
Output
一行结果。
Sample Input
Sample Output
HINT
数据规模和约定
0:A<=10,n<=10。
1..3:A<=1000,n<=20.
4..9:A<=10^9,n<=20
10..19:A<=10^9,n<=500。
全部:mod<=10^9,并且mod为素数,mod>A>n+1
Source
分析:
首先我们可以得出一个时间空间复杂度都爆炸的dp方程,大概可以拿到20分的样子...
首先我们将序列有序化,然后最后乘上$n!$...
定义$f[i][j]$为用$[1,i]$里面的数字组成$a[1]~a[j]$的答案...
那么$f[i][j]=f[i-1][j]+f[i-1][j-1]*i$...
然后我就不会了...跑去膜拜题解...http://blog.csdn.net/ta201314/article/details/52753481
发现有两种做法:拉格朗日插值法&容斥原理...
首先是拉格朗日插值法...
我们可以发现,所有的$f[i][j](i<j)$都是$0$,然后对于所有的$f[i][j](i>=j)$我们研究暴力转移的方程:$f[i][j]=i*sum _{k=1}^{i-1} f[k][j-1]$,发现这是一个$f[i][j]=i*s(i-1,j-1)$的形式,那么我们猜想$f[x][i]$是一个关于$x$的多项式,然后发现对于$f[x][i]$和$f[x][i-1]$,$f[x][i]$比$f[x][i-1]$的次数大二:一个是前缀和使得次数+1,另一个是乘了一个$x$又使得次数+1...所以其实$f[i][j]$就是一个最高项次数为$2j$的多项式,那么问题就是求出每一项的系数,用拉格朗日插值法来求...
拉格朗日插值法就是$f(x)=sum _{i=0}^{n} a_{i}x^{i}=sum _{i=0}^{n} f(x _{i})prod _{j=0}^{n} frac {x-x _{j}}{x _{i}-x _{j}}[i≠j]$
有了这个东西我们就可以在$O(2*n*n)$的时间复杂度内得出答案...
代码:
拉格朗日插值法:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=1000+5,maxm=500+5; int n,m,cnt,mod; long long x[maxn],y[maxn],f[maxn+maxm][maxm],fac[maxn]; long long power(long long x,int y){ long long ans=1; while(y){ if(y&1) (ans*=x)%=mod; (x*=x)%=mod,y>>=1; } return ans; } signed main(void){ scanf("%d%d%d",&m,&n,&mod); f[0][0]=1;fac[0]=1;cnt=0; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; for(int i=1;i<=n*3+5;i++) for(int j=0;j<=n;j++){ if(j) f[i][j]=(f[i-1][j]+f[i-1][j-1]*i%mod)%mod; else f[i][j]=f[i-1][j]; } if(n*3+5>=m){ printf("%lld ",f[m][n]*fac[n]%mod); return 0; } for(int i=1;cnt<=(n<<1)+1;i++) if(f[i][n]&&i!=m) x[++cnt]=i,y[cnt]=f[i][n]; long long a=1,b,ans=0; for(int i=1;i<=cnt;i++) (a*=(m-x[i]+mod)%mod)%=mod; for(int i=1;i<=cnt;i++){ b=(m-x[i]+mod)%mod; for(int j=1;j<=cnt;j++) if(i!=j) (b*=(x[i]-x[j]+mod)%mod)%=mod; (ans+=a*power(b,mod-2)%mod*y[i]%mod)%=mod; } printf("%lld ",ans*fac[n]%mod); return 0; }
By NeighThorn