前言
由于 NTT 是 101010 级算法,所以本篇题解仅介绍到 O(n2)O(n^2)O(n2) 解法。
Description
给定一个长度为 nnn 的排列 ppp。
令其中第 iii 个位置的权值为最长的包含 iii 的单调区间。例如,p=[4,1,2,3,7,6,5]p=[4,1,2,3,7,6,5]p=[4,1,2,3,7,6,5] 中,第 666 个位置的权值为 [5,7][5,7][5,7] 的长度,第 222 个位置的权值为 [2,4][2,4][2,4] 的长度。
将这些权值依次拼在一起,就得到了 ppp 的阶梯序列。
给定 aaa,你需要求出存在多少个 ppp,使得 aaa 为 ppp 的阶梯序列。
1≤n≤1051 \le n \le 10^51≤n≤105,时限 10s\texttt{10s}10s,空限 1GB\texttt{1GB}1GB。
Solution
Part 1: 性质的分析与观察
根据阶梯序列的定义,aia_iai 是 iii 所属的极长单调区间长度。但是 iii 既可以是这段区间的中间某个位置,也可以是两端的位置,考虑起来情况较多,较为困难。于是我们先考虑第一个位置,其只能作为单调区间的开头,思考起来较为容易。
令 x=a1x=a_1x=a1。不难发现,此时 [1,x][1,x][1,x] 是一个极长单调区间。接下来,我们再考虑 px+1p_{x+1}px+1,并令 x′=ax+1x'=a_{x+1}x′=ax+1。不难发现,px+1p_{x+1}px+1 不能与 [1,x][1,x][1,x] 合并成一个新的单调区间,且 [x+1,x+x′][x+1,x+x'][x+1,x+x′] 也是一个极长的单调区间。以此类推,我们将序列划分为了若干个极长的单调区间。这样一来,我们就成功地将问题转化为:给定若干个区间,你需要往里面填数,使得序列构成一个排列,且每个给定的区间都是一个极长的单调区间,求方案数。
如果区间只要求单调,不要求极长,那么问题就十分简单了。由于每个区间的 pip_ipi 在值域上都是连续的一段 [L,R][L,R][L,R],所以我们只需要钦定这些 [L,R][L,R][L,R] 的大小关系以及这些极长区间的增减性即可唯一确定 ppp。方案数不难通过组合数以及乘法原理写出。
若要求极长,问题变了复杂了起来。
注意到,若两段区间不可合并,那么限制会较为复杂;若必须合并,那么限制非常简单。这启发我们从反面考虑。但是,不可合并的相邻区间对太多了,这又启发我们容斥地计算。然而 nnn 较大,不能 2n2^n2n 地枚举相邻对。
能否 dp\text{dp}dp 呢?
Part 2: dp 的设计与转移
为方便叙述,定义 sis_isi 表示第 iii 个区间的长度。
令 fif_{i}fi 表示,仅考虑前 iii 个区间的方案数。
根据容斥的基本思想,对于第 i+1i+1i+1 个位置,它有两种选择:
- violate the rule\text{violate the rule}violate the rule——它与前面的区间可以合并;
- follow the rule or not\text{follow the rule or not}follow the rule or not——无论它填什么都可以。
考虑动态地将第 i+1i+1i+1 个区间插入进前 iii 个区间的按值域左端点排序的序列。然而,无论对于前者(违反规则)还是后者(随意),插入的位置数似乎较难根据 iii 直接确定(若插入到某个不恰当的位置,可能会将原来两个合并的区间断开)。
仔细分析——对于前者而言,若前面一个区间长度为 111,那么插入的位置有 222 个,否则只有 111 个;对于后者而言,令 jjj 为前 iii 个区间中钦定可合并的次数,那么插入的位置树为 i−j+1i-j+1i−j+1 个。
从而,我们加维度 jjj,表示目前钦定合并的次数。同时,再加一维 kkk,表示上一个区间的长度是否为 111。
总结一下最终的状态设计:令 fi,j,kf_{i,j,k}fi,j,k 表示,看了前 iii 个区间,合并 jjj 次,上一个区间的长度为 111 或不为 111 的方案数。注意,这里的上一个区间长度不一定是 si−1s_{i-1}si−1,而可能是一个比它更大的值(因为 si−1s_{i-1}si−1 可能参与了多次合并,而使得上一个区间的长度较大)。
转移如下:
-
钦定合并。
fi−1,j,0→fi,j+1,0f_{i-1,j,0} \to f_{i,j+1,0}fi−1,j,0→fi,j+1,02fi−1,j,1→fi,j+1,02f_{i-1,j,1} \to f_{i,j+1,0}2fi−1,j,1→fi,j+1,0 -
钦定不合并。
-
若 si≠1s_i \neq 1si=1,则
2(i−j)fi−1,j,0/1→fi,j,02(i-j)f_{i-1,j,0/1} \to f_{i,j,0}2(i−j)fi−1,j,0/1→fi,j,0 -
若 si=1s_i=1si=1,则
(i−j)fi−1,j,0/1→fi,j,1(i-j)f_{i-1,j,0/1} \to f_{i,j,1}(i−j)fi−1,j,0/1→fi,j,1
-
这里容易出现一个错误,就是在转移的时候漏掉系数 222。由于长度超过 111 的区间的单调增与单调减是不同的,所以在向第三维为 000 的状态转移时,要额外乘上一个 222。
边界:
①若 s1≠1s_1 \neq 1s1=1,f1,0,0=2f_{1,0,0}=2f1,0,0=2
②若 s1=1s_1=1s1=1,f1,0,1=1f_{1,0,1}=1f1,0,1=1。
答案:
∑i=0len(−1)i(fn,i,0+fn,i,1)\sum_{i=0}^{\text{len}} (-1)^i (f_{n,i,0}+f_{n,i,1})i=0∑len(−1)i(fn,i,0+fn,i,1)
时间复杂度 O(n2)O(n^2)O(n2)。
//https://codeforces.ml/contest/1553/submission/123423493
for (int i=2;i<=len;i++){
for (int j=0;j<=len;j++){
chksum(f[i-1][j][0],f[i][j+1][0]);
chksum(f[i-1][j][1],f[i][j+1][0],2);
if (s[i]!=1){
for (int k=0;k<2;k++) chksum(f[i-1][j][k],f[i][j][0],2*(i-j));
}
else{
for (int k=0;k<2;k++) chksum(f[i-1][j][k],f[i][j][1],i-j);
}
}
}

这篇博客探讨了一个算法问题,涉及长度为n的排列p的阶梯序列。阶梯序列由排列p中每个位置的最长单调区间的长度组成。文章首先分析了问题的性质,然后提出了动态规划的解决方案。通过定义状态表示前i个区间的合并情况,博客详细解释了状态转移方程,并给出了O(n^2)的时间复杂度的算法实现。最后,提供了求解方案数的公式。
234

被折叠的 条评论
为什么被折叠?



