[AGC043D] Merge Triplets
Genius_Star · · 题解
或许更好的阅读体验。
思路:
考虑找到最终什么样子的排列是合法的,可以被生成的,找一下性质。
一个比较显然的性质是:
- 对于每个数组的第一个元素,加入
p 时一定是作为前缀最大值出现的。
证明比较简单,因为它一直是
然后继续找性质,如果
-
且
a_{i, 2}, a_{i, 3} < a_{i, 1} 即a_{i, 1} 是三个中的最大值,那么接下来一定会把a_{i, 2}, a_{i, 3} 一起选了(因为a_{i, 1} 已经是开头中最小的了,把它删了加入更小的,那最小的肯定是那个更小的)。 -
否则其它情况,要么
a_{i, 2} 跟着a_{i, 1} 一起选,要么a_{i, 3} 跟着a_{i, 2} 一起选,或者a_{i, 1} < a_{i, 2} < a_{i, 3} 不会出现跟随的情况。
那这个跟随本质是什么,第一个本质上是在前缀最大值
所以回到最终序列
你发现这个条件是充要的,通过这个你一定可以构造出一组
-
前缀最大值后面跟两个的情况,直接三个放一块。
-
跟一个的情况,如果前面还有单独的前缀最大值,则随便拿个拼起来放一起就行,否则找一个后面的单独前缀最大值拼一起。
-
最后剩下的单独的,三个依次拼起来就行。
考虑怎么对这个计数?定义
- 直接放一个前缀最大值:
- 放一个前缀最大值跟一个非前缀最大值:
- 放一个前缀最大值跟两个非前缀最大值:
直接转移就是
完整代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define lowbit(x) (x) & (-(x))
#define popcnt(x) __builtin_popcount(x)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 6060;
inline ll read(){
ll x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')
f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
inline void write(ll x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
int n, m, mod;
int f[N][N];
int main(){
n = read(), mod = read();
m = (n * 3);
f[0][0] = f[1][0] = f[2][0] = f[2][1] = 1;
for(int i = 3; i <= m; ++i){
f[i][0] = 1;
for(int j = 1; j <= min(n, i >> 1); ++j){
f[i][j] = (f[i - 1][j] + 1ll * (i - 1) * f[i - 2][j - 1] % mod) % mod;
f[i][j] = (f[i][j] + 1ll * (i - 1) * (i - 2) % mod * f[i - 3][j - 1] % mod) % mod;
}
}
int ans = 0;
for(int j = 0; j <= n; ++j)
ans = (ans + f[m][j]) % mod;
write(ans);
return 0;
}