题目大意:对于长度为n的a[i]=i的数列,每次可以选择两个数字交换,问交换不超过k次之后会得到多少不同的排列。
首先考虑一个朴素dp,即dp[i][j]表示序列长度是i交换次数是j的答案,那么显然要么i不参与交换,即前n-1个元素自己玩;或者n最后在p位置,然后相当与前n-1个位置自己玩,但是n凑合了一次交换次数。
因此转移有:
dp[i][j]=dp[i−1][j]+(i−1)dp[i−1][j−1]
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
(
i
−
1
)
d
p
[
i
−
1
]
[
j
−
1
]
注意到如果固定交换次数,然后将dp数值看作关于序列长度的函数,即
fk(n)
f
k
(
n
)
,那么:
fk(n)=fk(n−1)+(n−1)fk−1(n−1)
f
k
(
n
)
=
f
k
(
n
−
1
)
+
(
n
−
1
)
f
k
−
1
(
n
−
1
)
或者干脆:
fk(x)=fk(x−1)+(x−1)fk−1(x−1)
f
k
(
x
)
=
f
k
(
x
−
1
)
+
(
x
−
1
)
f
k
−
1
(
x
−
1
)
,还是考虑归纳,当k=1时答案是x(x-1)/2,是个二次多项式;然后后半部分根据归纳就是2k-1次多项式,f_k就相当于是做这个2k-1次多项式的前缀和,因此是2k次的。暴力比较小的情况拉格朗日插值即可。
好像还有直接计数的办法,没有仔细研究。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define K 6020
#define lint long long
#define mod 1000000007
using namespace std;
int f[2][K],ps[K],ss[K],fac[K],facinv[K];lint xs[3];
inline int inv(int x,int k=mod-2,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int mol(lint x) { return x%=mod,(x<0?x+mod:x); }
int main()
{
int n,k,pre=0,now=1,ans=0;scanf("%d%d",&n,&k);
for(int i=0;i<=2*k+1;i++) f[pre][i]=1;f[now][0]=1;
for(int i=1;i<=k;i++,swap(now,pre))
for(int j=1;j<=2*k+1;j++)
f[now][j]=(f[now][j-1]+(j-1ll)*f[pre][j-1])%mod;
swap(now,pre),ps[0]=ss[2*k+2]=1,xs[0]=1ll,xs[1]=-1ll;
if(n<=2*k+1) return !printf("%d\n",f[now][n]);
for(int i=1;i<=2*k+1;i++) ps[i]=(lint)ps[i-1]*mol(n-i)%mod;
for(int i=2*k+1;i>=1;i--) ss[i]=(lint)ss[i+1]*mol(n-i)%mod;
for(int i=fac[0]=1;i<=2*k+1;i++) fac[i]=(lint)fac[i-1]*i%mod;
for(int i=facinv[0]=1;i<=2*k+1;i++) facinv[i]=inv(fac[i]);
for(int i=1;i<=2*k+1;i++)
ans=mol(ans+xs[(i+1)&1]*f[now][i]*ps[i-1]%mod*ss[i+1]%mod*facinv[i-1]%mod*facinv[2*k+1-i]);
return !printf("%d\n",ans);
}