[计数dp] [小清新] [AGC043D] Merge Triplets
posted on 2024-06-03 13:30:08 | under | source
令块 \(i\) 中的元素分别为 \(a_{i,1/2/3}\)。
假如 \(\forall i,a_{i,1}<a_{i,2}<a_{i,3}\),那么生成的排列 \(P\) 就是其升序排列。
考虑块内元素,若 \(a_{i,j}>a_{i,j+1}\) 则 \(P\) 中 \(a_{i,j},a_{i,j+1}\) 必然相邻、若 \(a_{i,1}>a_{i,2},a_{i,3}\) 则 \(a_{i,1},a_{i,2},a_{i,3}\) 必然相邻。
于是让满足条件块内的元素绑在一起,关注其第一个元素,那么就转化成升序排列了。
考虑判定 \(P\) 是否合法,将降序序列绑起来,那么其长度必须 \(=1/2/3\),且开头元素必然递增。
这是充要条件吗?手摸构造一下:首先排除长度 \(3\) 的,然后可以让长度 \(1\) 和长度 \(2\) 的放在一起,最后必然剩下若干 \(1\),其个数为 \(3\) 的倍数。
由于 \(2\) 只能与 \(1\) 配对,所以得出结论:只要 \(2\) 个数小于等于 \(1\) 个数即可。
怎么计数?记 \(f_{i,j}\) 表示考虑 \(i\) 个元素,\(1\) 与 \(2\) 个数之差为 \(j\) 的绑定方案。
转移:\(f_{i,j}=f_{i-1,j-1}+f_{i-2,j-1}\times (i-1)+f_{i-3,j-2}\times (i-1)\times (i-2)\)。
复杂度 \(O(n^2)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ADD(a, b) a = (a + (b)) % mod
const int N = 6e3 + 5;
int n, mod, f[N][N << 1], ans;
signed main(){
cin >> n >> mod, n *= 3;
f[0][n] = 1;
for(int i = 1; i <= n; ++i)
for(int j = 0; j <= 2 * n; ++j){
ADD(f[i][j], f[i - 1][j - 1]);
if(i >= 2) ADD(f[i][j], f[i - 2][j + 1] * (i - 1) % mod);
if(i >= 3) ADD(f[i][j], f[i - 3][j] * (i - 1) % mod * (i - 2) % mod);
}
for(int j = n; j <= 2 * n; ++j) ADD(ans, f[n][j]);
cout << ans;
return 0;
}

浙公网安备 33010602011771号