把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

AT5801 [AGC043D] Merge Triplets

题面传送门
首先我们来找一下这个题目有什么特殊性质。
考虑最后生成的排列\(A\),则这个\(A\)一定没有连续四个\(A_i\)满足\(A_i>A_{i+1}>A_{i+2}>A_{i+3}\),因为序列长度只有3。换句话说,我们将这个序列将前缀max分段,段的长度不会超过\(3\)
然而这个也不是充要条件。
我们发现如果长度为\(2\)的段如果大于长度为\(1\)的段的话,那么就会有段没法匹配。
加上这个就是充要条件了。
那么这个我们可以考虑\(dp\)解决。
我们设\(dp_{i,j}\)表示长度为\(i\)的序列,\(1\)\(2\)的差值为\(j\)的方案数。
这样有一个好处,就是因为最后段第一个的一定是\(i\),我们可以任意转移,而那些前面的我们可以考虑整体加一之类的让其合法。
code:

#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db double
#define N 6000
#define eps (1e-5)
#define mod 998244353
#define U unsigned
using namespace std;
int n,M,K;ll ans,dp[N+5][N+5<<1];
int main(){
	freopen("1.in","r",stdin);
	re int i,j;scanf("%d%d",&n,&M);K=3*n;dp[0][K]=1;
	for(i=1;i<=K;i++){
		for(j=0;j<=2*K;j++){
			if(j)dp[i][j]=dp[i-1][j-1];
			if(i>1) dp[i][j]+=dp[i-2][j+1]*(i-1)%M;
			if(i>2) dp[i][j]+=dp[i-3][j]*(i-1)*(i-2)%M;
			dp[i][j]%=M;
		}
	}
	for(i=K;i<=2*K;i++) ans+=dp[K][i];printf("%d\n",ans%M);
}
posted @ 2021-06-08 19:32  275307894a  阅读(42)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end