ABC309G Ban Permutation
ABC309G Ban Permutation
求有多少个 \(1\) 至 \(n\) 的排列 \(P\),满足对于任意 \(i\) 都有 \(|i-p_i|\ge X\)。
\(1\le n\le 100\),\(1\le X\le 5\)。
首先不难将题意转化成车的放置问题:
有一个 \(n\times n\) 的棋盘,要求放置 \(n\) 个互不攻击的车,对于第 \(i\) 行第 \(j\) 列,如果 \(|i-j|<X\),就不能放置车,求方案数。
由于 \(X\) 很小,\(|i-j|<X\) 这个条件就是非常好的,所以可以考虑容斥。
设 \(f_{i,j}\) 表示,我们考虑了前 \(i\) 行,至少放置 \(j\) 辆不合法的车的方案数。注意这里我们只计算不合法的车的方案数。那么显然答案为 \(\sum_{i=0}^{n}(-1)^if_{n,i}\)。
那么第 \(i\) 行的放置就有两种可能,不合法与合法。
如果不合法,那么我们就需要枚举不合法的车放置的位置,并且保证这个位置之前没被使用过。
但注意到这个位置的范围随着 \(i\) 的增加始终都是 \([i-X+1,i+X-1]\),所以就可以加入一个状态 \(S\) 表示这些位置的使用情况。
设 \(S'\) 为 \(i-1\) 行的状态,考虑 \(i-1\to i\) 的过程,\(i-1\) 行所有不合法的状态都要右移,第 \(i-1-X+1\) 个位置变成了合法位置不需要考虑,所以第 \(i\) 行的状态 \(S\) 即为 \(S'\) 右移一位的答案。
那么枚举这一行放置的不合法的位置 \(p\) 并保证 \(p\) 不在 \(S\) 中,令 \(T\) 为 \(S\) 加入 \(p\) 后的结果,那么显然 \(f_{i,j,T}=f_{i-1,j-1,S}\)。
而对于合法情况,直接继承过来第 \(i-1\) 行的答案即可,\(f_{i,j,S}=f_{i-1,j-1,S'}\)。
时间复杂度 \(O(n^24^XX)\)。
const int MAXN(110);
const int M(1024);
int n,x,m;
int fac[MAXN],f[MAXN][MAXN][M];
int main()
{
n=read(),x=read(),m=1<<(2*x-1);
fac[0]=1;
rep(i,1,n) fac[i]=mul(fac[i-1],i);
f[0][0][0]=1;
rep(i,1,n) rep(j,0,i-1) rep(S,0,m-1)
{
int T=S>>1;
f[i][j][T]=add(f[i][j][T],f[i-1][j][S]);
rep(k,0,2*x-2) if((!(T&(1<<k)))&&i+k+1-x>=1&&i+k+1-x<=n)
f[i][j+1][T|(1<<k)]=add(f[i][j+1][T|(1<<k)],f[i-1][j][S]);
}
int ans=0;
rep(i,0,n) rep(S,0,m-1)
{
int k=(i&1)?MOD-f[n][i][S]:f[n][i][S];
ans=add(ans,mul(k,fac[n-i]));
}
printf("%d\n",ans);
return 0;
}