【学习笔记】AGC043D Merge Triplets

不妨从必要条件入手,如果 a i > a i + 1 a_i>a_{i+1} ai>ai+1那么显然 i i i i + 1 i+1 i+1在同一块中,那么不妨做一个工作,按前缀 max ⁡ \max max将结果序列进行划分。

然后有一个神必的结论: c 1 ≥ c 2 c_1\ge c_2 c1c2(或者 c 2 + c 3 ≤ n c_2+c_3\le n c2+c3n),其中 c i c_i ci表示长度为 i i i的段的数目。证明考虑,将结果序列划分成 n n n个子序列,用 p i , j p_{i,j} pi,j表示第 i i i个子序列第 j j j个数的位置,那么 p i , 1 , p i , 2 , p i , 3 p_{i,1},p_{i,2},p_{i,3} pi,1,pi,2,pi,3递增并且对于 j = 2 , 3 j=2,3 j=2,3,满足 ( p i , j − 1 , p i , j ) (p_{i,j-1},p_{i,j}) (pi,j1,pi,j)之间的数均比 a p i , j a_{p_{i,j}} api,j小。显然划分的每一组的第一个数都是前缀最大值,因此构造是平凡的。那么方案数就是不同的划分数目。复杂度 O ( n 2 ) O(n^2) O(n2)

#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=6e3+5; 
int n,mod;
int f[N][N];
void add(int &x,int y){
	if((x+=y)>=mod)x-=mod;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>mod;f[3*n][0]=1;
	for(int i=3*n;i>0;i--){
		for(int j=0;j<=n;j++){
			if(f[i][j]){
				add(f[i-1][j],f[i][j]);
				if(i>=2)add(f[i-2][j+1],(ll)f[i][j]*(i-1)%mod);
				if(i>=3)add(f[i-3][j+1],(ll)f[i][j]*(i-1)%mod*(i-2)%mod);
			}
		}
	}int res(0);
	for(int i=0;i<=n;i++)add(res,f[0][i]);
	cout<<res;
} 
posted @ 2023-01-05 16:34  仰望星空的蚂蚁  阅读(21)  评论(0)    收藏  举报  来源