这道题有两问,第一问直接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;
}