1. 题意
给定一个 1∼N 的排列 P,求有多少个非空子序列是 kadomatsu-like。
对于一个序列 a=(a1,a2,…,ak):
若 ai−1<ai>ai+1,则 ai 是一个峰;若 ai−1>ai<ai+1,则 ai 是一个谷。
设峰的数量为 x,谷的数量为 y,如果 x>y,则称这个序列是 kadomatsu-like。
要求统计满足条件的子序列数量,对 998244353 取模。
N≤3e5,P 是一个排列。
2. 思路
从题目已知的条件入手,注意到 P 是一个排列,所以任意两个元素都不相同。
因此对于一个子序列来说,相邻两项之间只有两种关系:上升:ai<ai+1;下降:ai>ai+1。
如果把峰谷和相邻关系对应起来,那么峰对应:上升 → 下降;谷对应:下降 → 上升。
也就是说,峰谷都只出现在相邻关系发生变化的位置。
因此,原问题的关键就是刻画这些变化对 x−y 的贡献。
不妨记:
si={+1−1ai<ai+1ai>ai+1
若 ai−1,ai,ai+1 是一个峰,那么:si−1=+1,si=−1
若 ai−1,ai,ai+1 是一个谷,那么:si−1=−1,si=+1
接着令:ti=2si−1−si
那么:ti=1,说明当前位置是一个峰; ti=−1,说明当前位置是一个谷;
所以 x−y 就可以表示成 ti 的和:x−y=∑i=2k−1ti
把式子展开化简:
i=2∑k−1ti=2(s1−s2)+(s2−s3)+⋯+(sk−2−sk−1)=2s1−sk−1
于是 x>y 等价于:2s1−sk−1>0
因为 si 只可能是 +1 或 −1,所以这等价于:s1>0,sk−1<0
也就是:a1<a2,ak−1>ak
所以原问题就从统计峰谷数量,转化成了统计满足“开头上升,结尾下降”的子序列。
考虑一个满足条件的子序列。真正决定它是否合法的,其实只有四个关键位置:
a<b≤c<d
这里的 a,b,c,d 都是原排列中的下标。其中:
- Pa 是子序列的第一个元素;
- Pb 是子序列的第二个元素;
- Pc 是子序列的倒数第二个元素;
- Pd 是子序列的最后一个元素。
当子序列长度为 3 时,第二个元素和倒数第二个元素是同一个元素,因此 b=c。
因为我们前面推出了:Pa<Pb,Pc>Pd
于是可以固定中间两个关键位置 b,c,分别统计左边和右边的选择数量。
为了满足开头上升,需要在 b 左边选一个比 Pb 小的元素作为第一个元素。
- 设:Li=∣{j∣j<i, Pj<Pi}∣
为了满足结尾下降,需要在 c 右边选一个比 Pc 小的元素作为最后一个元素。
- 设:Ri=∣{j∣j>i, Pj<Pi}∣
接下来分类讨论贡献情况。
所以总答案可以写成:
Ans=c=1∑NRc(Lc+b<c∑Lb⋅2c−b−1)
这里外层枚举的是倒数第二个关键位置 c。其中:
如果每次都重新计算内层求和,本质上还是在枚举所有 (b,c),复杂度为 O(N2),需要继续优化。
记:Sc=b<c∑Lb⋅2c−b−1。
再展开下一项:Sc+1=b<c+1∑Lb⋅2c−b=2b<c∑Lb⋅2c−b−1+Lc=2Sc+Lc。
因此可以线性递推维护 Sc,总答案变成:Ans=c=1∑NRc(Lc+Sc)
最后,L 和 R 数组可以用权值树状数组进行预处理统计。
最终复杂度为 O(NlogN)。
3. 代码部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| ll read( ) { }
struct BIT { } bit;
int p[maxn], L[maxn], R[maxn];
void solve( ) { int n = read( );
for( int i = 1; i <= n; ++ i ) { p[i] = read( ); }
bit.init( n ); for( int i = 1; i <= n; ++ i ) { L[i] = bit.ask( p[i] - 1 ); bit.add( p[i], 1 ); }
bit.init( n ); for( int i = n; i >= 1; -- i ) { R[i] = bit.ask( p[i] - 1 ); bit.add( p[i], 1 ); }
ll ans = 0; ll Si = 0;
for( int r = 1; r <= n; ++ r ) { ans = ( ans + R[r] * ( L[r] + Si ) % mod ) % mod; Si = ( Si * 2 + L[r] ) % mod; }
cout << ans << '\n'; }
|
4. 复盘