题意,给定n,k,求有多少排列是的 | p[i]-i |=1 的数量为k。
Solution
直接dp会有很大的后效性。
所以我们考虑固定k个数字使得它们是合法的,所以我们设dp[i][j][0/1][0/1]表示前i个数,填了j个数,当前位置有没有被选,下一位有没有被选,这样做的话,转移会比较简单。
那么除去这j个数,剩下的数随便填,乘上全排列就好了。
但这样会多算。
然后这种问题有一个容斥模型,直接套上就好了。
#include<iostream> #include<cstdio> #define N 1002 using namespace std; typedef long long ll; int n,k; ll dp[N][N][2][2],jie[N],ni[N],g[N],ans; const int mod=1e9+7; ll calc(int n,int m){ return jie[n]*ni[m]%mod*ni[n-m]%mod; } ll power(ll x,int y){ ll ans=1; while(y){ if(y&1)(ans*=x)%=mod; (x*=x)%=mod; y>>=1; } return ans; } int main(){ scanf("%d%d",&n,&k);jie[0]=1; for(int i=1;i<=n;++i)jie[i]=(jie[i-1]*i)%mod;ni[n]=power(jie[n],mod-2); for(int i=n-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod; dp[0][0][1][0]=1; for(int i=1;i<=n;++i){ for(int j=0;j<=n;++j){ dp[i][j][0][0]=(dp[i-1][j][0][0]+dp[i-1][j][1][0])%mod; dp[i][j][1][0]=(dp[i-1][j][0][1]+dp[i-1][j][1][1])%mod; if(j){ (dp[i][j][0][0]+=dp[i-1][j-1][0][0])%=mod; dp[i][j][0][1]+=(dp[i-1][j-1][0][0]+dp[i-1][j-1][1][0])%mod; dp[i][j][0][1]%=mod; (dp[i][j][1][0]+=dp[i-1][j-1][0][1])%=mod; dp[i][j][1][1]+=(dp[i-1][j-1][0][1]+dp[i-1][j-1][1][1])%mod; dp[i][j][1][1]%=mod; } } } for(int i=k;i<=n;++i) g[i]=(dp[n][i][0][0]+dp[n][i][1][0])%mod*jie[n-i]%mod; for(int i=k;i<=n;++i)(ans+=(((i-k)&1)?-1:1)*calc(i,k)*g[i]%mod+mod)%=mod; ans=(ans+mod)%mod; cout<<ans; return 0; }
原文地址:https://www.cnblogs.com/ZH-comld/p/9896870.html
时间: 2024-10-15 15:53:44