HDU6143 Killer Names(DP 与 容斥原理)

题意:m个字符组成2个长度分别为n的名和字,要求名和字不能有相同的字符,有多少可能

1.容斥原理:对于名我们选i个字符(这i个字符都要用到);对于字我们就选剩下的所有字符(任意选取其中一些字符使用);对于字的种数使用快速幂模,对于名我们定义f函数,f[i]表示只使用i种字母的情况,f[i]=i^n-f[i-1]*C(i,i-1)-f[i-2]*C(i, i-2)…-f[1]*C(i,1);

因此最终的结果便是 ∑ i = 1 m − 1 C ( m , i ) ∗ f [ i ] ∗ ( m − i ) n \sum_{i=1}^{m-1}C(m,i)*f[i]*(m-i)^n i=1m1C(m,i)f[i](mi)n

代码:

#include<bits/stdc++.h>
#include<cstdio>
const int mod=1e9+7;
typedef long long ll;
ll C[2006][2006];
ll f[2005];
using namespace std;
void Cnm()
{
	    int len=2005;
		C[1][1]=1;
		for(int i=2; i<len; i++)
		{
			C[i][1]=i;
			for(int j=2	; j<=i; j++)
			{
				C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
			}
		}
}

ll quick_pow_mod(ll a, ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}

void fi(ll n)
{
	f[1]=1;
	for(int i=2; i<=n; i++)
	{
		ll temp=0;
		for(int j=1; j<i; j++)
		{
			temp=(temp+C[i][j]*f[j]%mod)%mod;
		}
		f[i]=(quick_pow_mod(i, n)+mod-temp)%mod;
	}
}
int main()
{
    Cnm();
	int T;
	scanf("%d", &T);
	while(T--)
	{
		ll n,m,ans;
		scanf("%lld%lld", &n,&m);
		fi(n);
		if(n<m)     //当n<m时,i最大可以取到n
		{
			ans=0;
            for(int i=1; i<=n; i++) 
			{
				ans=(ans+C[m][i]*f[i]%mod*quick_pow_mod(m-i,n)%mod)%mod;
			}
		}
		else                  //否则i最大可以取到m-1
		{
		     ans=0;
			for(int i=1; i<=m-1; i++)
			{
				ans=(ans+C[m][i]*f[i]%mod*quick_pow_mod(m-i, n)%mod)%mod;
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

思路2:dp,dp[i][j]表示长度为i使用了j种字符的可能,递推方程:

dp[i][j]=dp[i-1][j-1]*(m-j+1)+dp[i-1][j]*j

代码:

#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll dp[2005][2005];
ll quick_mod(ll a,ll b)
{
	ll res=1;
	while(b) 
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		int n,m;
		ll ans=0;
		scanf("%d%d", &n, &m);
		dp[1][1]=m;
		for(int i=2; i<=n; i++)  // n<m && n>=m
		{
			for(int j=1; j<=i && j<m; j++)
			{
				dp[i][j]=(dp[i-1][j]*j%mod+dp[i-1][j-1]*(m-j+1)%mod)%mod;
			}
		}
     	for(int i=1; i<m; i++)
        	{
	        	ans=(ans+dp[n][i]*quick_mod(m-i,n)%mod)%mod;
        	}
        	printf("%lld\n", ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值