[BZOJ2655]calc-容斥原理-拉格朗日插值
阿新 • • 發佈:2019-01-10
calc
Description
一個序列a1,…,an是合法的,當且僅當:
長度為給定的n。
a1,…,an都是[1,A]中的整數。
a1,…,an互不相等。
一個序列的值定義為它裡面所有數的乘積,即a1a2…an。
求所有不同合法序列的值的和。
兩個序列不同當且僅當他們任意一位不一樣。
輸出答案對一個數mod取餘的結果。
Input
一行3個數,A,n,mod。意義為上面所說的。
Output
一行結果。
Sample Input
9 7 10007
Sample Output
3611
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
蒟蒻完全不會伯努利數的做法,只會插值QWQ
思路:
考慮容斥,設表示長度為時的答案。
那麼令,有一個簡單的容斥:
上式的含義是,列舉至少有多少個數相等,並容斥這種情況的貢獻。
然後考慮的求法。
顯然這是個自然數冪和問題。
根據差分提供的資訊,是一個關於的次多項式。
因此預處理,對於每個進行一次的拉格朗日插值即可~
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=509;
ll a,n,md;
ll fac[N],inv[N],f[N],s[N];
inline ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)ret=ret*a%md;
a=a*a%md;b>>=1;
}
return ret;
}
inline void init()
{
fac[0]=1;
for(int i=1;i<N;i++)
fac[i]=fac[i-1]*i%md;
inv[N-1]=qpow(fac[N-1],md-2);
for(int i=N-1;i>=1;i--)
inv[i-1]=inv[i]*i%md;
}
inline ll b(ll k)
{
static ll w[N],y[N],l,ans;
for(int i=1;i<=k+2 && i<=a;i++)
y[i]=qpow(i,k);
for(int i=2;i<=k+2 && i<=a;i++)
y[i]=(y[i]+y[i-1])%md;
if(a<=k+2)return y[a];
l=1;
for(int i=1;i<=k+2;i++)
l=l*(a-i)%md;
for(int i=1;i<=k+2;i++)
w[i]=(((k+2-i)&1?-1ll:1ll)*inv[i-1]*inv[k+2-i]%md+md)%md;
ans=0;
for(int i=1;i<=k+2;i++)
(ans+=w[i]*qpow(a-i,md-2)%md*y[i]%md)%=md;
return (ans*l%md+md)%md;
}
inline ll c(ll a,ll b)
{
return fac[a]*inv[b]%md*inv[a-b]%md;
}
int main()
{
scanf("%lld%lld%lld",&a,&n,&md);
init();
f[0]=1;
for(int i=1;i<=n;i++)
{
s[i]=b(i);
for(int j=1;j<=i;j++)
(f[i]+=(j&1?1:-1)*c(i-1,j-1)*s[j]%md*f[i-j]%md*fac[j-1]%md+md)%=md;
}
printf("%lld\n",f[n]);
return 0;
}