calc (容斥+拉格朗日插值+dp)

一個序列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

思路:

考慮dp
dp[i]爲長度爲i時的結果
dp[i]怎麼表示呢,考慮這i個裏面可能有多少個相等的值
相等的值的個數可能是j=(1,2,3,4,…i),進行容斥即可
這j個相等的數的值可能是p=(1,2,3,4,…a),應該是p的j次冪的和,用拉格朗日插值求次冪之和

代碼:

typedef long long ll;
using namespace std;
const int MAXN=550;
const int inf=0x3f3f3f3f;
//const int mod=1e9+7;
//::iterator it;
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
ll n,A,mod;
ll quick(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=(ans*a)%mod;
		a=(a*a)%mod;
		b/=2;
	}
	return ans;
}
ll inv(ll x){
	return quick(x,mod-2);
}
ll f[MAXN];
ll fac[MAXN];
ll iiik(ll n,ll k){

    for(ll i=1;i<=k+2;i++){
        f[i]=(f[i-1]+quick(i,k))%mod;
    }
    if(n<=k+2){
        return f[n];
    }
    ll p=1;
    for(ll i=1;i<=k+2;i++){
        p=(p*(n-i))%mod;
    }
    ll ans=0;
    for(ll i=1;i<=k+2;i++){
        ll m=1;
        // ll v1=inv(n-i)%mod;
        // ll v2=inv(fac[i-1]%mod*fac[k+2-i]%mod)%mod;
        if((k+2-i)%2==1)m=-1;
        m=((m*p%mod*f[i]%mod)*inv((n-i)*fac[i-1]%mod*fac[k+2-i]%mod))%mod;
        //ans=(ans+m*v1*v2%mod*f[i]%mod*p%mod)%mod;
        ans=(ans+m)%mod;
    }
    return (ans+mod)%mod;
}
ll ccc(ll n,ll m){
    if(n<m)return 0;
    return (fac[n] * quick(fac[m], mod - 2) % mod * quick(fac[n - m], mod - 2) % mod)%mod;
}
ll s[MAXN];
ll dp[MAXN];
int main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>A>>n>>mod;
    fac[0]=1;
    for(ll i=1;i<=505;i++)fac[i]=(fac[i-1]*i)%mod;
        dp[0]=1;
    for(ll i=1;i<=n;i++){
        s[i]=iiik(A,i);
        for(ll j=1;j<=i;j++){
            ll m=1;
            if((j-1)&1)m=-1;
            dp[i]=((dp[i]+m*ccc(i-1,j-1)*fac[j-1]%mod*s[j]%mod*dp[i-j]%mod)%mod+mod)%mod;
        }
    }
    cout<<(dp[n]+mod)%mod<<endl;
    return 0;
}
//i 1---n (-1)^(j-1) *ccc(i-1,j-1)*(j-1)!*s[j]*dp[i-j]
/*
9 7 10007
*/

大佬的題解
dp+拉格朗日的做法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章