这种排列生成排列的题目我们一般可以考虑生成排列合法的充要条件。

首先可以发现的一点就是该生成排列的任意一个数 \(p_i\) 一定不存在连续的三个数 \(p_{i + 1}, p_{i + 2}, p_{i + 3}\) 比其小。考虑原排列的生成方式,如果存在生成的排列中 \(p_{i + 1} < p_i\) 那么在原排列中 \(p_{i + 1}\) 一定和 \(p_i\) 在同一块,而如果 \(p_{i + 2} < p_i\) 说明 \(p_{i + 2}\) 和 \(p_i\) 在同一块中,以此类推,因为一个块大小最多为 \(3\) 所以一定不存在对于任意一个数 \(p_i\) 一定不存在连续的三个数 \(p_{i + 1}, p_{i + 2}, p_{i + 3}\) 比其小。并且我们还可以发现每次都是一个大的数 \(p_i\) 后面跟上两个或一个或不跟任何比他小的数,之后再出现一个比 \(p_i\) 更大的数,下面我们将一个大的数 \(p_i\) 和其后面跟的小的数看作生成排列中的一个段。

上面这个条件充分吗?感觉不充分,实际上确实不充分。比如说 \(2 \ 4 \ 3 \ 6 \ 5 \ 8 \ 7 \ 9 \ 1\) 就不是一个合法的生成排列,但上面的第一个条件给了我们启发,让我们从生成排列的每个段来观察。于是我们可以用生成排列每个段的角度来看上面那个不合法的排列,不难发现 \(2 \ 4 \ 3\) 是一个段,\(6 \ 5\) 是一个段,\(8 \ 7\) 是一个段,\(9 \ 1\) 是一个段。根据我们的性质一,生成排列中的每一个段必然来自原排列中的同一个块,这里长度为 \(3\) 的段有 \(1\) 个,长度为 \(2\) 的段有 \(3\) 个,长度为 \(3\) 的段已经占用了原排列的一个块,但剩下 \(3\) 个长度为 \(2\) 的段往哪里放?显然没有地方可以同时放下 \(3\) 个长度为 \(2\) 的块,于是我们这里可以猜想长度为 \(3\) 的段和长度为 \(2\) 的段的数量不能超过 \(n\) 是一个合法条件。因为长度为 \(3\) 的段和长度为 \(2\) 的段都需要占用原排列的一个块,因此如果两者总数大于 \(n\) 这是不可能的。但如果两者长度小于 \(n\) 就是一个合法的生成排列了吗?确实是的,因为我们可以从前往后考虑每个段并还原原排列,每次我们将当前的一个段放入原排列中任意一个可填的位置即可,这样我们就构造出了一个合法的原排列。

因此我们的原问题转化为:问有多少个长度为 \(n\) 的排列满足其是由若干个长度为 \(1 / 2 / 3\) 的段组成的并且段开头的数不断单调递增,段的定义是开头的数大于其后面接的数。

于是咱们可以考虑使用 \(dp\) 来进行计数,令 \(dp_{i, j, k}\) 表示已经填完了前 \(i\) 个数,当前填到的最大数为 \(j\),当前长度为 \(2\) 和长度为 \(3\) 的段已经填了 \(k\) 个的方案。转移的化就只需要考虑往后面插入一个长度为 \(1 / 2 / 3\) 的段枚举上次的最大值 \(l\) 即可,加上前缀和可以做到 \(\mathcal{O(n ^ 3)}\)。

上面这个 \(dp\) 还是不行,我们可以考虑另一个排列计数的方法,为了能去掉 \(j\) 哪一维的限制,我们可以考虑 有限制的排列 这题的计数方式,令 \(dp_{i, j}\) 表示由 \(1 \sim i\) 组成的排列,长度为 \(2 / 3\) 的段之和的数量已经为 \(j\) 的方案数,这样做的好处就是我们能保证当前段首是最大的。同样我们考虑往后插入长度为 \(1 / 2 / 3\) 的段,如果插入长度为 \(1\) 的段,只能是插入 \(i\),那么有转移 \(dp_{i, j} = dp_{i - 1, j}\)。假设插入长度为 \(2\) 的段,那么我们还是只能将 \(i\) 作为最大值,接下来保持 \(i\) 不动,往后插入剩下 \(i - 1\) 中的任意一个并将之前大于他的数 \(+1\)(\(i\) 保持不变)这样也能保持排列的性质,即 \(dp_{i, j} = dp_{i - 2, j - 1} \times (i - 1)\)。同理可以得到插入长度为 \(3\) 的段的转移方程 \(dp_{i, j} = dp_{i - 3, j - 1} \times (i - 1) \times (i - 2)\),这样我们能做到 \(\mathcal{O(n ^ 2)}\) 了。

#include<bits/stdc++.h>
using namespace std;
#define N 6000 + 5
#define M 2000 + 5
#define rep(i, l, r) for(int i = l; i <= r; ++i)
int n, ans, Mod, dp[N][M];
int read(){
char c; int x = 0, f = 1;
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Inc(int a, int b){
return (a += b) >= Mod ? a - Mod : a;
}
int Mul(int a, int b){
return 1ll * a * b % Mod;
}
int main(){
n = read(), Mod = read();
dp[0][0] = 1;
rep(i, 1, 3 * n) rep(j, 0, min(i / 2, n)){
dp[i][j] = Inc(dp[i][j], dp[i - 1][j]);
if(i >= 2 && j >= 1) dp[i][j] = Inc(dp[i][j], Mul(dp[i - 2][j - 1], i - 1));
if(i >= 3 && j >= 1) dp[i][j] = Inc(dp[i][j], Mul(dp[i - 3][j - 1], Mul(i - 1, i - 2)));
}
rep(i, 0, n) ans = Inc(ans, dp[3 * n][i]);
printf("%d", ans);
return 0;
}

AT5801 [AGC043D] Merge Triplets的更多相关文章

  1. [AGC043-D]Merge Triplets

    题目   点这里看题目. 分析   我们不妨来考虑一下生成的序列有什么性质.   为了方便表示,我们将序列\(S\)的第\(i\)项写为\(S[i]\).   首先考虑如果所有的\(A\)序列都是递增 ...

  2. [算法]——归并排序(Merge Sort)

    归并排序(Merge Sort)与快速排序思想类似:将待排序数据分成两部分,继续将两个子部分进行递归的归并排序:然后将已经有序的两个子部分进行合并,最终完成排序.其时间复杂度与快速排序均为O(nlog ...

  3. SQL 提示介绍 hash/merge/concat union

    查询提示一直是个很有争议的东西,因为他影响了sql server 自己选择执行计划.很多人在问是否应该使用查询提示的时候一般会被告知慎用或不要使用...但是个人认为善用提示在不修改语句的条件下,是常用 ...

  4. Merge Sorted Array

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note:Yo ...

  5. SQL Tuning 基础概述06 - 表的关联方式:Nested Loops Join,Merge Sort Join & Hash Join

    nested loops join(嵌套循环)   驱动表返回几条结果集,被驱动表访问多少次,有驱动顺序,无须排序,无任何限制. 驱动表限制条件有索引,被驱动表连接条件有索引. hints:use_n ...

  6. Git 少用 Pull 多用 Fetch 和 Merge

    本文有点长而且有点乱,但就像Mark Twain Blaise Pascal的笑话里说的那样:我没有时间让它更短些.在Git的邮件列表里有很多关于本文的讨论,我会尽量把其中相关的观点列在下面. 我最常 ...

  7. Merge 的小技巧

    今天跟大家分享一下搬动数据使用Merge的方法. 有些时候,当我们做数据搬动的时候,有时候做测试啊,换对象啊,就会存在有时候外键存在,不知道怎么对应的关系.比如我现在有架构相同的两组table , A ...

  8. [LeetCode] Merge Sorted Array 混合插入有序数组

    Given two sorted integer arrays A and B, merge B into A as one sorted array. Note:You may assume tha ...

  9. [LeetCode] Merge Intervals 合并区间

    Given a collection of intervals, merge all overlapping intervals. For example, Given [1,3],[2,6],[8, ...

随机推荐

  1. RabbitMQ学习笔记三:Java实现RabbitMQ之与Spring集成

    搭建好maven项目环境,加入RabbitMQ依赖包 <dependency> <groupId>org.springframework.amqp</groupId> ...

  2. 使用 DML语句,对 “锦图网” 数据进行操作,聚合函数练习

    查看本章节 查看作业目录 需求说明: 根据客户 ID 统计订单数.订单总金额.最高订单金额.最低订单金额和每份订单平均金额,并按订单总金额升序显示 根据客户统计订单总订购人次数> 5 的统计信息 ...

  3. Java基础周测一、二(50题)

    一.单选题 (共50题,250分) 1.下列选项不可作为Java语言变量名的是(    ). A. a1 B. $1 C. _1 D. 21 正确答案: D 2.有一段Java应用程序,它的类名是a1 ...

  4. javaScript系列 [43]-TS、Class and ES5

    本文讨论Typescript中的Class同ES5构造函数的对应关系,涉及TypeScript的诸多语法.构造函数.面向对象以及原型对象等相关知识点细节,本文只简单对比并不进行深入展开. TypeSc ...

  5. JdbcTemplate 基本使用

    简介 JdbcTemplate 是 Spring 对 JDBC 的封装,目的是使 JDBC 更加易于使用.JdbcTemplate 是 Spring 的一部分.JdbcTemplate 处理了资源的建 ...

  6. centos6.5 搭建zabbix3.0

    一.搭建LAMP平台 注意zabbix3.0需要php5.4以上的版本 (1)安装依赖包 httpd和mysql安装过程省略 rpm -e httpd --nodeps yum -y install ...

  7. [Blue Prism] Data item 的使用

      1.用于存储各种数据类型的变量,支持的数据类型如下: Binary - 用于存储二进制 blob,例如文本文件. Date - 用于存储日期. 此数据类型与 DateTime 类似,只是 Date ...

  8. 利用ajaxfileupload插件异步上传文件

    html代码: <input type="file" id="imgFile" name="imgFile" /> js代码: ...

  9. 痞子衡嵌入式:我入选了2021年度与非网(eefocus)星选创作者Top10

    本周二「与非网」一个美女运营小姐姐加痞子衡微信,告知痞子衡评上了一个奖,让痞子衡把收件地址告诉她,她把证书寄过来. 昨天痞子衡收到了快递,拆开一看,原来是被评上了 与非网 2021 年度创作者,这个证 ...

  10. 深度介绍Flink在字节跳动数据流的实践

    本文是字节跳动数据平台开发套件团队在1月9日Flink Forward Asia 2021: Flink Forward 峰会上的演讲分享,将着重分享Flink在字节跳动数据流的实践. 字节跳动数据流 ...