题目:http://codeforces.com/contest/285/problem/E
是2018.7.31的一场考试的题,当时没做出来。
题解:http://www.cnblogs.com/yanshannan/p/9410986.html
因为那个值对于 i 位置来说只和 i 位置放了 i-1 或 i+1 有关,所以状态里记录一下 i 和 i+1 有没有已经放过,再加上 i-1 的对于 i-1 和 i 的状态,就能转移了。
枚举这一位:放 i-1 /放 i+1/先空下。先空下对那个值无影响,所以可以做到;最后相当于指定了 j 个位置放什么值,剩下的位置乘上一个排列即可。
随便往空下的位置放可能导致多一些值,所以最后容斥一下即可。
注意初值赋给那个状态!
那场考试的其余信息就见 Zinn 的博客吧。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1005,mod=1e9+7; int n,m,dp[N][N][2][2],jc[N],jcn[N],ans,f[N]; int pw(int x,int k) {int ret=1;while(k){if(k&1ll)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1ll;}return ret;} void init() { jc[0]=1; for(int i=1;i<=n;i++) jc[i]=(ll)jc[i-1]*i%mod; jcn[n]=pw(jc[n],mod-2); for(int i=n-1;i>=0;i--) jcn[i]=(ll)jcn[i+1]*(i+1)%mod; } void upd(int &x){x-=(x>=mod?mod:0);} int C(int n,int m){return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod;} int main() { scanf("%d%d",&n,&m); init(); dp[0][0][1][0]=1;//so pos1 can't 0!!!!!! for(int i=1;i<=n;i++) for(int j=0;j<=n;j++)//n for(int k=0;k<=1;k++) { dp[i][j][k][0]=dp[i-1][j][0][k]+dp[i-1][j][1][k], upd(dp[i][j][k][0]);//rand if(j)dp[i][j][k][1]=dp[i-1][j-1][0][k]+dp[i-1][j-1][1][k], upd(dp[i][j][k][1]),//i+1 dp[i][j][k][0]+=dp[i-1][j-1][0][k], upd(dp[i][j][k][0]);//i-1 } for(int i=m;i<=n;i++) f[i]=(ll)(dp[n][i][0][0]+dp[n][i][1][0])*jc[n-i]%mod; ans=f[m]; for(int i=m+1,fx=-1;i<=n;i++,fx=-fx) ans+=(ll)f[i]*C(i,i-m)%mod*fx,ans+=(fx==1?0:mod),upd(ans); printf("%d ",ans); return 0; }