【学习笔记】AGC043D Merge Triplets

博客围绕算法展开,从必要条件出发,介绍按前缀max对结果序列进行划分的方法。给出一个结论,即长度为i的段的数目满足一定关系,并进行了证明。最后指出方案数为不同的划分数目,复杂度为O(n2)。

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

然后有一个神必的结论:c1≥c2c_1\ge c_2c1c2(或者c2+c3≤nc_2+c_3\le nc2+c3n),其中cic_ici表示长度为iii的段的数目。证明考虑,将结果序列划分成nnn个子序列,用pi,jp_{i,j}pi,j表示第iii个子序列第jjj个数的位置,那么pi,1,pi,2,pi,3p_{i,1},p_{i,2},p_{i,3}pi,1,pi,2,pi,3递增并且对于j=2,3j=2,3j=2,3,满足(pi,j−1,pi,j)(p_{i,j-1},p_{i,j})(pi,j1,pi,j)之间的数均比api,ja_{p_{i,j}}api,j小。显然划分的每一组的第一个数都是前缀最大值,因此构造是平凡的。那么方案数就是不同的划分数目。复杂度O(n2)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;
} 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值