【bzoj4321】queue2 dp
题目描述
n 个沙茶,被编号 1~n。排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 1(+1 或-1)就行;
现在想知道,存在多少方案满足沙茶们如此不苛刻的条件。
输入
只有一行且为用空格隔开的一个正整数 N,其中 100%的数据满足 1≤N ≤ 1000;
输出
一个非负整数,表示方案数对 7777777 取模。
样例输入
4
样例输出
2
题解
dp
老套路了,考虑把数从小到大插入的过程进行dp。
设 $f[i][j]$ 表示 $1\sim i$ 的排列,有 $j$ 组相邻的相差1,且 $i$ 和 $i-1$ 不相邻的方案数;
设 $g[i][j]$ 表示 $1\sim i$ 的排列,有 $j$ 组相邻的相差1,且 $i$ 和 $i-1$ 相邻的方案数。
那么考虑插入 $i+1$ 的位置,有:不破坏空位且不与 $i$ 相邻、不破坏空位且与 $i$ 相邻、破坏空位且不与 $i$ 相邻、破坏空位且与 $i$ 相邻(只发生在 $g$ 的转移)4种。分别推一下方案数即可。
最后的答案就是 $f[n][0]$ 。
时间复杂度 $O(n^2)$ 。
另外把前几项丢到oeis上可以得到线性递推式 $a_n=(n+1)a_{n-1}-(n-2)a_{n-2}-(n-5)a_{n-3}+(n-3)a_{n-4}$ ,就能 $O(n)$ 求解了,感觉像是某容斥然而并不能推出来...
#include <cstdio> #define mod 7777777 long long f[1010][1010] , g[1010][1010]; int main() { int n , i , j; scanf("%d" , &n); f[1][0] = 1; for(i = 1 ; i < n ; i ++ ) { for(j = 0 ; j < i ; j ++ ) { f[i + 1][j] = (f[i + 1][j] + f[i][j] * (i - j - 1)) % mod; g[i + 1][j + 1] = (g[i + 1][j + 1] + f[i][j] * 2) % mod; if(j) f[i + 1][j - 1] = (f[i + 1][j - 1] + f[i][j] * j) % mod; f[i + 1][j] = (f[i + 1][j] + g[i][j] * (i - j)) % mod; g[i + 1][j + 1] = (g[i + 1][j + 1] + g[i][j]) % mod; if(j) f[i + 1][j - 1] = (f[i + 1][j - 1] + g[i][j] * (j - 1)) % mod; g[i + 1][j] = (g[i + 1][j] + g[i][j]) % mod; } } printf("%lld\n" , f[n][0]); return 0; }