CF1526 Oolimry and Suffix Array 题解

· · 题解

题意

给定长度为 n,字符集大小为 k 的字符串的 sa 数组(后缀数组)。求符合条件的字符串 S 的个数,对 998244353 取模。

#### Sol 先由 $sa$ 数组推出 $rk$ 数组,不难发现,如果 $rk_i > rk_j$,则 $S_i \geqslant S_j$,问题在于能不能取等。考虑到如果取等,在 $i$ 和 $j$ 代表的后缀对比大小时,将会忽视相同的第一位,从第二位开始比较,因此取等的条件是 $rk_{i+1} > rk_{j+1}$。我们只需对所有 $sa_i,sa_{i+1}$ 进行比较,就能确定整个字符串任意两个字符的大小关系,而且肯定合法。 接下来就是计算方案数,假设有 $m$ 个字符可以与它在 $sa$ 中的前一个字符取等号。每次有一个取了等,值不同的字符就减少一个,设有 $i$ 个取等,剩下的都不取等,则方案数为 $\tbinom{m}{i} \tbinom{k}{n-i}$,其中前者是选 $i$ 个取等的方案数,后者是给 $n-i$ 个字符赋不同值的方案数。 总方案数为 $\sum\limits^m_{i=0} \tbinom{m}{i} \tbinom{k}{n-i}$。这个已经可以算了。 由于 $m \leqslant n$,当 $i > m$ 时 $\tbinom{m}{i} = 0$,因此可以写成 $\sum\limits^n_{i=0} \tbinom{m}{i} \tbinom{k}{n-i} = \tbinom{m+k}{n}$。计算这个更简单,时间复杂度 $O(n+k)$。 #### AC代码 ```cpp #include <cstdio> const int N = 200005, mod = 998244353; int n, k, m, sa[N], rk[N], inv[N], ans = 1; int main(void) { scanf("%d%d", &n, &k), rk[sa[0] = n+1] = 0, inv[1] = 1; for(int i = 1;i <= n;++i) scanf("%d", sa+i), rk[++sa[i]] = i; for(int i = 2;i <= n;++i) m += rk[sa[i]+1] > rk[sa[i-1]+1]; for(int i = m+k;i > m+k-n;--i) ans = 1ll*ans*i%mod; for(int i = 2;i <= n;++i) ans = 1ll*ans*(inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod)%mod; //计算逆元同时更新答案 printf("%d\n", ans); return 0; } ```