ShinriiTin/contest/agc005

AtCoder Grand Contest 005

D - ~K Perm Counting

题目大意:

求有多少个\(1\)\(n\)的排列\(a\),满足\(\forall 1\le i\le n\to|a_i-i|\not=k\).

\(2\le n\le2000,1\le k\le n-1\).

题解:

\(f[i]\)表示选择\(i\)个数并决定它们的位置,使得这\(i\)个数都满足\(|a_i-i|=k\),即\(a_i\)可以是\(i-k\)\(i+k\),的方案数。

则由容斥原理可得\(\displaystyle ans=\sum\limits_{i=0}^n f[i]\times(n-i)!\times(-1)^i\).

接下来介绍如何计算\(f[i]\).

\(1\)\(n\)\(\mod{k}\)的余数分组,则不同的组之间相互独立。

对于同组的情况,我们考虑一个dp,令\(g[i][j][mask]\)表示只考虑前\(i\)个数,一共选了\(j\)个数,最右边的两个数是否占用了\(x+k\)位置的状态为\(mask\)的方案数。

则有递归基:

\[g[1][0][0]=1,g[1][1][1]=1\]

以及转移方程:

\[\displaystyle\begin{aligned}g[i][j][0]&=g[i-1][j][0]+g[i-1][j][2]+g[i-1][j-1][0]\\g[i][j][1]&=g[i-1][j-1][0]+g[i-1][j-1][2]\\g[i][j][2]&=g[i-1][j][1]+g[i-1][j][3]+g[i-1][j-1][1]\\g[i][j][3]&=g[i-1][j-1][1]+g[i-1][j-1][3]\end{aligned}\]

\(getS(i,j)=g[i][j][0]+g[i][j][2]\),则可以做一个分组背包来计算\(f[i]\)

枚举\(\mod{k}\)的余数\(x\),然后枚举这一组选择的数量\(i\),则背包时应该用\(f[i]+=f[i-j]\times getS(siz[x],i)\)更新,其中\(siz[x]\)为该组的大小。

计算\(g\)部分的时间复杂度为\(O(n^2)\),空间复杂度为\(O(n^2)\);计算\(f\)部分的时间复杂度为\(O(n^2)\),空间复杂度为\(O(n)\);计算\(ans\)部分的时间复杂度为\(O(n)\),空间复杂度为\(O(n)\)

因此,算法总的时间复杂度为\(O(n^2)\),空间复杂度\(O(n^2)\)