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