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+拉格朗日的做法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值