题目

  点这里看题目。

分析

  我们不妨来考虑一下生成的序列有什么性质。

  为了方便表示,我们将序列\(S\)的第\(i\)项写为\(S[i]\)。

  首先考虑如果所有的\(A\)序列都是递增的,那么我们得到的序列肯定是递增的。如果存在递减的情况,例如其中某个序列\(B\in\{A_1,A_2,\dots,A_n\}\),存在\(B[1]>B[2]\)。那么按照取数规则,我们一旦取出了\(B[1]\),我们就一定会取出\(B[2]\)。这个比较显然。这是因为\(B[1]\)被取出的时候,其他所有序列的第一个元素肯定都大于\(B[1]\),因此也肯定大于\(B[2]\)。

  我们发现只要序列中存在\(B[1]>B[2]\)或者\(B[2]>B[3]\),这两个数就会被相邻地取出;如果存在\(B[1]>B[2]\)且\(B[1]>B[3]\),这三个数也会被相邻取出。我们将这种必然相邻取出的情况分进一个组里面。

  注意到组只会有长度为 1 ,长度为 2 ,长度为 3 三种,而且由于一个长度为 2 的组一定和一个长度为 1 的组成对出现,因此长度为 2 的组的数量一定不超过长度为 1 的组的数量。

  我们可以发现,这样的组在构造的过程中,一定会按照组的第一个元素的大小进行排序构造出一个排列来。因此,一些组如果合法,就可以唯一确定一个排列。因此,我们可以通过计算组的合法构造方案来计算可生成的排列方案数。

  我们有两种方法来解决这个问题:

1.DP

  我们需要将\([1,3n]\)划分成若干组,限制如下:

   1. 每一组的长度不超过3。

   2. 每一组的第一个数一定是这一组中最大的。

   3. 长度为 2 的组的数量不超过长度为 1 的组的数量。

  因此我们可以设计如下的 DP 方案:

  \(f(i,j)\):前\(i\)个数分组,满足长度为 1 的组的数量减去长度为 2 的组的数量为\(j\)的方案数。

  转移实际上是考虑最后一个数会怎样分组。转移如下:

\[f(i,j)=f(i-1,j-1)+(i-1)f(i-2,j+1)+(i-1)(i-2)f(i-3,j)
\]

  答案是\(\sum_{i=0}^{3n}f(3n,i)\)。DP 的时间是\(O(n^2)\)。

2.枚举

  这其实是我自己口胡的。

  建议写第一种方法

  由于\(n\)很小,我们可以直接枚举长度为 2 的组的数量和长度为 3 的组的数量(需要满足长度为 2 的组的数量不超过长度为 1 的数量这一前提)。设\(f(n)\)为\(2n\)个数全部分为长度为 2 的组的方案数。转移大概如下:

\[f(n)=f(n-1)+(2n-2)(2n-3)f(n-2)
\]

  \(g(n)\)为\(3n\)个数全部分为长度为 3 的组的方案数,转移类似。这样预处理完之后就可以枚举组的数量了。答案:

\[\sum_{i=0}^{\lfloor\frac {3n}2\rfloor}\sum_{j=0}^{\lfloor\frac{3n-2i}3\rfloor}[3n-2i-3j\ge2i]C_{3n}^{2i}\times C_{3n-2i}^{3j}\times f(i)\times g(j)
\]

  时间是\(O(n^2)\)。

  如果有问题请轻喷,我也没有试过这个方法。

代码

#include <cstdio>

const int MAXN = 6005;

template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} int f[MAXN][MAXN << 1];
int N, M; void add( int &x, const int v ) { x += v; if( x >= M ) x -= M; } int main()
{
read( N ), read( M );
int t = N * 3;
f[0][t] = 1;
for( int i = 1 ; i <= t ; i ++ )
for( int j = - t ; j <= t ; j ++ )
{
if( j > -t ) add( f[i][j + t], f[i - 1][j + t - 1] ); //长度为1
if( j < t && i >= 2 ) add( f[i][j + t], 1ll * f[i - 2][j + t + 1] * ( i - 1 ) % M ); //长度为2
if( i >= 3 ) add( f[i][j + t], 1ll * f[i - 3][j + t] * ( i - 1 ) % M * ( i - 2 ) % M ); //长度为3
}
int ans = 0;
for( int i = 0 ; i <= t ; i ++ ) add( ans, f[t][i + t] );
write( ans ), putchar( '\n' );
return 0;
}

[AGC043-D]Merge Triplets的更多相关文章

  1. AT5801 [AGC043D] Merge Triplets

    这种排列生成排列的题目我们一般可以考虑生成排列合法的充要条件. 首先可以发现的一点就是该生成排列的任意一个数 \(p_i\) 一定不存在连续的三个数 \(p_{i + 1}, p_{i + 2}, p ...

  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. Sql Server数据库导入Excel、txt数据详解,新人必看

    转自个人原创 https://blog.csdn.net/qq_15170495/article/details/104591606 数据库的要想导入数据,列的映射很是关键,只有列名匹配好,系统才知道 ...

  2. Poj1753 翻转棋子

    这个题就是用枚举举遍所有情况,然后一个一个深搜看看是不是符合条件,符合条件直接退出,不符合则继续, 由于表格只有16个所以可以得知最多的步数只能是16,所以可以根据步数从0到16依次枚举, 第一个符合 ...

  3. 使用element-ui 的table 组件 出现表格线条不对齐的问题

    在全局css样式中添加以下代码即可: body .el-table th.gutter { display: table-cell !important }

  4. Java——变量自增(++)自减(--)

    //运算符在操作数之后,称为“后增量”.i变量自增,返回自增之前的值;//运算符在操作数之前,称为“前增量”.i变量自增,返回自增之后的值.//自减同理 public static void test ...

  5. docker的file内容解释

    关键字---重点啊) FROM 基础镜像,当前新镜像是基于哪个镜像的 MAINTAINER  镜像维护者的姓名和邮箱地址 RUN  容器构建时需要运行的命令 EXPOSE 当前容器对外暴露的端口 WO ...

  6. ECharts使用教程

    引入 ECharts ECharts 3 开始不再强制使用 AMD 的方式按需引入,代码里也不再内置 AMD 加载器.因此引入方式简单了很多,只需要像普通的 JavaScript 库一样用 scrip ...

  7. 【Ubuntu】Ubuntu系统启动过程中,输入用户名与密码后登录一直卡在紫色界面问题(未解决,最后通过重装系统)

    0. 前言 由于本电脑为公用电脑,可能由于其他人点了图像界面中推荐的内核更新,导致原来安装的NVIDIA显卡驱动 430 与升级后的 5.0 内核不兼容,从而导致输入用户名后登录一直卡在紫色界面.在排 ...

  8. python异常1

    异常的层次结构: BaseException [所有异常的基类] +-- SystemExit [解释器请求退出] +-- KeyboardInterrupt [用户中断执行(通常是输入^C)] +- ...

  9. Java中的集合(十一) 实现Map接口的TreeMap

    Java中的集合(十一) 实现Map接口的TreeMap 一.TreeMap简介(基于JDK1.8) TreeMap是基于红黑树数据结构,是一个key-value的有序集合,该映射根据其键的自然顺序进 ...

  10. Parrot os KDE还是MATE版本

    在经历了KDE桌面痛苦折磨后,准备转投MATE的怀抱,不得不说Parrot KDE的ram的占有和windows 10差不多,大量的图形化处理,让我本来不多的内存更加血上加霜. 所以,关于版本的推荐, ...