正题
这两天第一题都考Dp我也是醉。
我们考虑一个点被算进答案,当且仅当中有东西移到这个点而且不走,假设这个点是最后移到这个点中的第一个。
那么我们考虑中的数必须先移走,我们设为第i步,x点在j,x-1点在k的方案数。
当然先枚举x。初始状态就是
接着就是一个转移的问题。
很明显可以让x向前走一步,让x-1向前走一步,或者让剩下的n-2个走一步。
当x-1走到0的时候,我们可以让剩下的n-1个走一步。
注意,在转移过程中j必须严格大于k。
接着我们的答案就是。
这样发现枚举x很耗时间,我们就可以直接顺着推,让x在1位置,x-1在0位置的状态倒推回去。
注意每次走一步,要从后往前递推,否则每次就不止走一步了。
最后算一下答案就可以了,因为k<=1000,多考虑一下边界情况,就可以通过这题。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
long long n;
long long f[1010][1010];
const long long mod=1e9+7;
long long m;
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
int main(){
scanf("%lld %lld",&n,&m);
f[1][0]=1;
for(int i=1;i<=m;i++)
for(int j=min((long long)i+1,n);j>=1;j--)
for(int k=min(i+1-j,j-1);k>=0;k--){
(f[j+1][k]+=f[j][k])%=mod;
(f[j][k+1]+=f[j][k])%=mod;
f[j][k]=f[j][k]*(n-2+(k==0))%mod;
}
long long ans=0;
for(int i=0;i<=m;i++) if(f[i+1][i]) (ans+=((long long)(1+n-i)*(n-i)/2)%mod*f[i+1][i]%mod)%=mod;
printf("%lld\n",ans*ksm(n,mod-1-m)%mod);
}