Codeforces 题面传送门 & 洛谷题面传送门

首先题目中涉及排列的 interval,因此可以想到析合树。由于本蒟蒻太菜了以至于没有听过这种神仙黑科技,因此简单介绍一下这种数据结构:我们注意到排列的区间有一个性质:对于排列中的两段区间 \(X,Y\),如果它们有交,那么必然有 \(X\cap Y,X\cup Y,X\setminus(X\cap Y),Y\setminus(X\cap Y)\) 四个集合均为区间,也就是说连续段之间只有包含没有相交关系,因此它们可以表示为一棵树形结构。

我们考虑用一棵根节点为区间 \([1,n]\),叶子节点为每个长度为 \(1\) 的区间的树表示这个树形结构,对于每个区间我们定义它的本原连续段为极大的、彼此之间不存在部分相交的连续段,举个例子,排列 \([5,1,4,2,3]\) 有两个本原连续段:\([5],[1,4,2,3]\)——显然我们能够找到这样的连续段组成的集合。那么我们就令这个区间的儿子为这些本原连续段们,继续递归下去即可建出这棵树。由于这棵树的叶子节点恰有 \(n\) 个,因此这棵树的节点数也是线性的。

考虑将这棵树的节点分分类,由于每个节点的儿子们都是一个个区间,因此我们可以将它们离散化成一个个在 \([1,\text{儿子个数}]\) 之内的数,我们称这样得到的排列为儿子排列,手玩几组数据即可发现对于每个点而言,它的儿子排列总共只有两种类型,否则就不满足“本原连续段”的定义了:

  • 儿子排列从左到右恰好为 \(1,2,3,\cdots,\text{儿子个数}\) 或者 \(\text{儿子个数},\cdots,3,2,1\),我们称这样的点为合点
  • 儿子排列中除了整个区间和长度为 \(1\) 的子区间不存在任何其他连续段,我们称这样的点为析点

比方说排列 \([9,1,10,3,2,5,7,6,8,4]\) 建出树来如下图所示:

析合树有以下性质:

  • 每个析点儿子个数一定 \(\ge 4\),因为任何长度为 \(3\) 的排列都存在非平凡连续段
  • 如果我们指定一棵树上每个节点的析合性,并满足析点儿子个数 \(\ge 4\),合点儿子个数 \(\ge 2\),那么一定存在某个排列对应这棵树

回到此题来,此题等价于求儿子个数为 \(n\),且根为析点的排列个数 \(f_n\),直接求不太容易,因此考虑正难则反,那总排列数减去不合法的排列个数,前者就是 \(n!\),后者可以分情况讨论:

  1. 根是析点,那么我们可以枚举根节点的儿子个数 \(c\ge 4\),那么我们要将 \(n\) 个节点划分成 \(c\) 个区间,每个区间内的元素随便乱排,最后还要将这 \(c\) 个区间排成一列满足不存在非平凡区间,很显然我们可以将这个任务分成两部分,划分儿子和确定儿子排列,后者方案数显然就是 \(f_c\),前者可以设一个 \(s_{i,j}\) 表示将 \(i\) 个节点划分成 \(j\) 段的方案数,显然有 \(s_{i,j}=\sum\limits_{k<i}s_{i-k,j-1}·k!\)
  2. 根是合点,那么我们不妨假设根节点的儿子排列为 \(1,2,3,\cdots\),对于单调递减的情况乘个 \(2\) 即可,根据合点的定义必然存在某个前缀 \(i\) 满足 \(p[1...i]\) 恰好为 \([1,i]\) 的排列,我们就考虑枚举这个最小的 \(i\),记 \(g_i\) 为长度为 \(i\) 的、且存在某个长度不等于 \(i\) 的前缀 \(p[1...j]\) 为 \([1,j]\) 的排列的排列 \(p\) 的个数,那么有 \(g_i=i!-\sum\limits_{j<i}g_j(i-j)!\),根是合点的总数也就自然是 \(2g_n\)。

简单递推一下即可,复杂度三方。

const int MAXN=400;
int mod,fac[MAXN+5],ifac[MAXN+5],dp[MAXN+5],s[MAXN+5][MAXN+5],f[MAXN+5];
void init(int n){
for(int i=(fac[0]=ifac[0]=ifac[1]+1);i<=n;i++) ifac[i]=1ll*ifac[mod%i]*(mod-mod/i)%mod;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod,ifac[i]=1ll*ifac[i-1]*ifac[i]%mod;
f[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++) f[i]=(f[i]+1ll*f[j]*fac[i-j])%mod;
f[i]=(fac[i]-f[i]+mod)%mod;
} s[0][0]=1;
for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) for(int k=1;k<=i;k++)
s[i][j]=(s[i][j]+1ll*s[i-k][j-1]*fac[k])%mod;
dp[2]=(dp[1]=(dp[3]=0)+1)+1;
for(int i=4;i<=n;i++){
int sum1=0,sum2=0;
for(int j=1;j<i;j++) sum1=(sum1+1ll*f[j]*fac[i-j])%mod;
for(int j=4;j<i;j++) sum2=(sum2+1ll*dp[j]*s[i][j])%mod;
int sub=(2ll*sum1+sum2)%mod;dp[i]=(fac[i]-sub+mod)%mod;
}
}
int main(){
int qu;scanf("%d%d",&qu,&mod);init(MAXN);
while(qu--){
int n;scanf("%d",&n);
printf("%d\n",dp[n]);
}
return 0;
}

Codeforces 1089I - Interval-Free Permutations(析合树计数)的更多相关文章

  1. Codeforces 1093E Intersection of Permutations (CDQ分治+树状数组)

    题意:给你两个数组a和b,a,b都是一个n的全排列:有两种操作:一种是询问区间在数组a的区间[l1,r1]和数组b的区间[l2,r2]出现了多少相同的数字,另一种是交换数组b中x位置和y位置的数字. ...

  2. Codeforces 500D New Year Santa Network(树 + 计数)

    D. New Year Santa Network time limit per test 2 seconds memory limit per test 256 megabytes input st ...

  3. [Codeforces 266E]More Queries to Array...(线段树+二项式定理)

    [Codeforces 266E]More Queries to Array...(线段树+二项式定理) 题面 维护一个长度为\(n\)的序列\(a\),\(m\)个操作 区间赋值为\(x\) 查询\ ...

  4. [Codeforces 280D]k-Maximum Subsequence Sum(线段树)

    [Codeforces 280D]k-Maximum Subsequence Sum(线段树) 题面 给出一个序列,序列里面的数有正有负,有两种操作 1.单点修改 2.区间查询,在区间中选出至多k个不 ...

  5. codeforces 1217E E. Sum Queries? (线段树

    codeforces 1217E E. Sum Queries? (线段树 传送门:https://codeforces.com/contest/1217/problem/E 题意: n个数,m次询问 ...

  6. Codeforces 311D Interval Cubing 数学 + 线段树 (看题解)

    Interval Cubing 这种数学题谁顶得住啊. 因为 (3 ^ 48) % (mod - 1)为 1 , 所以48个一个循环节, 用线段树直接维护. #include<bits/stdc ...

  7. Codeforces Round #285 (Div.1 B & Div.2 D) Misha and Permutations Summation --二分+树状数组

    题意:给出两个排列,求出每个排列在全排列的排行,相加,模上n!(全排列个数)得出一个数k,求出排行为k的排列. 解法:首先要得出定位方法,即知道某个排列是第几个排列.比如 (0, 1, 2), (0, ...

  8. Codeforces Round #337 Alphabet Permutations

    E. Alphabet Permutations time limit per test:  1 second memory limit per test:  512 megabytes input: ...

  9. Codeforces 588E. A Simple Task (线段树+计数排序思想)

    题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...

随机推荐

  1. 这部分布式事务开山之作,凭啥第一天预售就拿下当当新书榜No.1?

    大家好,我是冰河~~ 今天,咱们就暂时不聊[精通高并发系列]了,今天插播一下分布式事务,为啥?因为冰河联合猫大人共同创作的分布式事务领域的开山之作--<深入理解分布式事务:原理与实战>一书 ...

  2. 使用flink实现一个简单的wordcount

    使用flink实现一个简单的wordcount 一.背景 二.需求 三.前置条件 1.jdk版本要求 2.maven版本要求 四.实现步骤 1.创建 flink 项目 2.编写程序步骤 1.创建Str ...

  3. Pogo-Cow S

    这题出在单调队列优化dp里,就离谱好吧...... 对不住了上来先喷一波,不过离谱是确实的 dp的含义也很简单,就是说从j到i的分数最大值 直接上代马,里面说的很详细了 1 #include<b ...

  4. [luogu2973]driving out the piggies 驱逐猪猡【高斯消元+概率DP】

    看到题面的那一刻,我是绝望的ORZ 图论加概率期望加好像不沾边的高斯消元???我人直接傻掉 还没学过概率期望的我果断向题解屈服了(然后还是傻掉了两节课来找线性方程.. Description 奶牛们建 ...

  5. Qt字符编码小知识

    1.VS2010默认编码是GBK,Qt5的内置编码是utf-8,想要在VS2010及其以上版本,优雅的使用utf-8的字符编码需要 // Coding: UTF-8(BOM) #if defined( ...

  6. 字符串匹配(kmp+trie+aho-corasic automaton+fail tree)

    目录 kmp 那么怎么快速求最长前缀后缀呢 trie aho-corasic automaton fail tree kmp 对于一个字符串\(s_{0\dots n}\),称\(s_{0\dots ...

  7. jQuery常用验证

    1.文本框不能为为空 if ($("#RushStartTime").val() == "") { alert("请输入该产品.."); $ ...

  8. Python推导式详解,带你写出比较精简酷炫的代码

    Python推导式详解,带你写出比较精简酷炫的代码 前言 1.推导式分类与用法 1.1 列表推导 1.2 集合推导 1.3 字典推导 1.4 元组推导?不存在的 2.推导式的性能 2.1 列表推导式与 ...

  9. LeetCode刷题 字符串详解

    一.字符串常用的操作 1. string类 1.1 string的定义与初始化 1.1.1 string的定义 1.1.2 string的初始化 1.2 string的赋值与swap.大小操作.关系运 ...

  10. Xshell Error Report,Program has stopped working

    xftp和xshell突然都无法运行并报错如图 图中的意思是,xshell有错误,官方想收集错误.可是也不能给你发送了,还这样啊. 解决办法 1.卸载Xshell和Xftp,重新安装. 参考:http ...