dp+第一类stirling数--51nod1250 排列与交换

传送门

这道题有两问,第一问直接dp就行,和stirling有一些关系的是第二问


(1)

solution:

设f[i][j]表示到第i个数已经交换了j次

因为必须是相邻交换,所以i可以不断向前交换,那么f[i][j]就可以由f[i-1][p](p=j-i+1~j)的和转移到

再用前缀和优化就能n^2转移了

最后统计答案的时候,只要看i和k的奇偶性是否相同,相同的话就可以重复交换相同的达到k次

 

(2)

首先关于第一类stirling数

(看到网上大都相似就直接引用了)

定义:s(n,k)的一个的组合学解释是:将n个物体排成k个非空循环排列(非空的)的方法数。

S(p, k)的递推公式:S(n, k)= (n - 1) * S(n- 1, k) + S(n - 1, k - 1).

              边界条件:S(n, 0) = 0, n >= 1.

  S(n, n) = 1, n>= 0.(有n个人和n个圆圈,每个圆圈就只有一个人)

我们考虑第n个物品。

1.该物品自己可以成一个非空循环排列,也就是S(k-1,n-1)种。

2.该物品也可以加到已经形成的k个非空循环排列中,共可以放在其他n- 1个物品的任意一个的一个位置。也就是( n - 1) * S(n- 1, k)种。

代码和杨辉三角求C(n,k)相似,要用long long,因为大于20种就会超int

 

solution:

这一问其实可以延续上一问的解法,也是dp

f[i][j]表示与上一问相同,i可以不换,也可以和前面任意一个换

因此转移就是 f[i][j]=f[i-1][j]+f[i-1][j-1]*(i-1)

 

如果用stirling考虑的话

可以考虑抽象建图,有n个点,每个位置上的数向这个位置的标号连边

可以知道最多有n个环,最少有n-k个

此时用stirling数的解法解出f数组,统计答案从f[n][n-k]加到f[n][n]就行了

 

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 3005
#define int long long
using namespace std;
int n,k,f[maxn][maxn],ans;
const int mod=1000000007;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

signed main(){
	n=rd(); k=rd();
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			f[i][j]=f[i-1][j];
			if(j-i>=0 && j) (f[i][j]=f[i-1][j]-f[i-1][j-i]+mod)%=mod;
		}
		for(int j=1;j<=k;j++)
			(f[i][j]+=f[i][j-1])%=mod;
	}
	for(int i=0;i<=k;i++)
		if(i%2==k%2) (ans+=f[n][i])%=mod;
	printf("%lld ",ans);
	memset(f,0,sizeof f); f[0][0]=1;
	for(int i=1;i<=n;i++){
		f[i][0]=f[i-1][0];
		for(int j=1;j<=k;j++){
			f[i][j]=(f[i-1][j]+f[i-1][j-1]*(i-1)%mod)%mod;
//			f[i][j]=(f[i-1][j-1]+f[i-1][j]*(i-1)%mod)%mod;第一类stirling数写法
		}
	}
	ans=0;
	for(int i=0;i<=k;i++) (ans+=f[n][i])%=mod;
//	for(int i=n-k;i<=n;i++) (ans+=f[n][i])%=mod;
	printf("%lld\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值