【BZOJ4321】queue2
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4321
根据zhhx大神的说法,这是一道套路题。。。
设dp[i][j][0]表示插完i,有j对相邻且差为1的数对,i与i-1不相邻;dp[i][j][1]表示插完i,有j对相邻且差为1的数对,i与i-1相邻。
然后我们分类讨论,先考虑推dp[i][j][1],因为他要求i与i-1相邻,所以情况更少些。
若i-1与i-2相邻,则i有两种方法,放在i-1与i-2之间,数对数量并不会改变,放在i-1另一边会产生一对数对,因此方案数+=dp[i-1][j][1]+dp[i-1][j-1][1];
若i-1与i-2不相邻,则i需要放到i-1两边,且会产生一对数对,方案数+=dp[i-1][j-1][0]*2。
然后考虑dp[i][j][0],相对会麻烦一些。
若i-1与i-2相邻,且放上i后会破坏一对数对,则共有j+1对数对之间放完会破坏一对,而i-1与i-2那一对之间不可以放,因此有j个位置。
若i-1与i-2不相邻,且放上i后会破坏一对数对,此时就有j+1个位置了。
若i-1与i-2相邻,且放上i不会破坏数对,原先有i个位置(此时放好i-1个元素),除去j个会破坏数对的位置和i-1与i-2的另一边(与i-2相隔i-1),共有i-j-1个位置。
若i-1与i-2不相邻,且放上i不会破坏数对,原先有i个位置,j个位置会破坏数对,i-1两边都不可以去,所有有i-j-2个位置。
综上,dp[i][j][0]=dp[i-1][j+1][1]*j+dp[i-1][j+1]*(j+1)+dp[i-1][j][1]*(i-j-1)+dp[i-1][j][0]*(i-j-2)。
然后考虑边界,显然dp[1][0][0]=1,而且当i=1时,也就这么一个合法状态。
1 #include <cstdio> 2 3 typedef long long ll; 4 5 const int maxn = 1005, mod = 7777777; 6 7 ll dp[maxn][maxn][2]; 8 9 int main() { 10 int n; 11 scanf("%d", &n); 12 dp[1][0][0] = 1; 13 for (int i = 2; i <= n; ++i) 14 for (int j = 0; j <= n; ++j) { 15 dp[i][j][0] = (dp[i - 1][j + 1][1] * j + dp[i - 1][j + 1][0] * (j + 1)) % mod; 16 dp[i][j][0] = (dp[i][j][0] + dp[i - 1][j][0] * (i - j - 2) + dp[i - 1][j][1] * (i - j - 1)) % mod; 17 dp[i][j][1] = dp[i - 1][j][1]; 18 if (j >= 1) dp[i][j][1] = (dp[i][j][1] + dp[i - 1][j - 1][1] + dp[i - 1][j - 1][0] * 2) % mod; 19 } 20 printf("%lld", dp[n][0][0]); 21 return 0; 22 }