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. 【UE4 C++ 基础知识】<5> 容器——TArray

    概述 TArray 是UE4中最常用的容器类.其速度快.内存消耗小.安全性高. 其设计时未考虑扩展问题,因此建议在实际操作中勿使用 新建(new) 和 删除(delete) 创建或销毁 TArray ...

  2. 【UE4 C++】读写Text文件 FFileHelper

    CoreMisc.h 读取 FFileHelper::LoadFileToString 读取全部内容,存到 FString FString TextPath = FPaths::ProjectDir( ...

  3. 如何接入 K8s 持久化存储?K8s CSI 实现机制浅析

    作者 王成,腾讯云研发工程师,Kubernetes contributor,从事数据库产品容器化.资源管控等工作,关注 Kubernetes.Go.云原生领域. 概述 进入 K8s 的世界,会发现有很 ...

  4. 服务器端的GPU使用

    服务器端的GPU使用 查看GPU信息 查看nvidia GPU信息: # 输入指令 lspci | grep -i nvidia # 结果如下: # 04:00.0 3D controller: NV ...

  5. js--数组的 fill() 填充方法详解

    前言 我们知道了很多了初始化数组的方法,但是初始化数组之后,数组中的每一项元素默认为 empty 空位占位,如何对数组这些空位添加默认的元素,ES6提供了 fill() 方法实现这一操作.本文总结数组 ...

  6. 2021.9.22考试总结[NOIP模拟59]

    T1 柱状图 关于每个点可以作出两条斜率绝对值为\(1\)的直线. 将绝对值拆开,对在\(i\)左边的点\(j\),\(h_i-i=h_j-j\),右边则是把减号换成加号. 把每个点位置为横坐标,高度 ...

  7. Spring Cloud Gateway夺命连环10问?

    大家好,我是不才陈某~ 最近有很多小伙伴私信我催更 <Spring Cloud 进阶>,陈某也总结了一下,最终原因就是陈某之前力求一篇文章将一个组件重要知识点讲透,这样导致了文章篇幅很长, ...

  8. Python AttributeError: module 'string' has no attribute 'atoi'

    python2 中可以用string.atoi 在python3中会报错 替换的方案是 string.atoi(your_str) 替换为 int(your_str) 这个代码python2和pyth ...

  9. LCA-离线tarjan模板

    /* *算法引入: *树上两点的最近公共祖先; *对于有根树的两个结点u,v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u,v的祖先且x的深度尽可能大; *对于x来说,从u到v的路径一定 ...

  10. jacoco-统计代码覆盖率并生成报告

    一.概述: 作为一个合格的测试人员,保证产品的软件质量是其工作首要目标,为了这个目标,测试人员常常会通过很多手段或工具来加以保证,覆盖率就是其中一环比较重要的环节. 通常我们会将测试覆盖率分为两个部分 ...