E. Oolimry and Suffix Array

E. Oolimry and Suffix Array

题目大意

给定 \(n\)\(k\) 和一个长度为 \(n\) 的后缀数组 \(s_0,s_1,\cdots,s_{n-1}\)
你需要求出当字符集 \(\Sigma\) 的大小为 \(k\) 时、有多少个长度为 \(n\) 的字符串会产生这样的后缀数组,答案对 \(998244353\) 取模。
\(1\leq n,k\leq2\times10^5;0\leq s_i\leq n-1\)\(s_i\) 互不相同。

如果你不知道什么是后缀数组:
在本题中,假设 \(s\) 为长度是 \(n\) 的字符串,那么我们设第 \(i\)\(s\) 的后缀为 \(s[i\cdots n-1]\)。将所有 \(s\) 的后缀按照字典序排序,得到的字符串序列按顺序对应的编号组成的序列就是字符串 \(s\) 的后缀数组。例如,对于字符串 \(s=\texttt{"oolimry"}\),其后缀数组为 \([3,2,4,1,0,5,6]\),因为将其后缀排序后为 \([\texttt{imry,limry,mry,olimry,oolimry,ry,y}]\)
我们在本题中定义字符串 \(a\) 比字符串 \(b\) 的字典序小 \((a\not=b)\),仅当下列条件之一成立:

  • \(a\)\(b\) 的前缀。
  • 存在整数 \(i\) 使得 \(a_i<b_i\) 且对于任意整数 \(j\) 使得 \(1\leq j<i\)\(a_j=b_j\) 成立。

分析

利用了,简单的后缀数组的性质。

先由sa数组推出rk数组,不难发现,如果\(rk_i>rk_j\),则\(S_i\geq S_j\),问题在于能不能取等。如果取等,则我们实际在比较ij代表的后缀对比大小时,将会忽略相同的第一位,从第二位开始比较,因此取等的条件是\(rk_{i+1}>rk_{j+1}\)。我们只需对所有\(sa_i,sa_{i+1}\)进行比较,就能确定整个字符串任意两个字符的大小关系,而且肯定合法。

接下来计算方案数,假设有m个字符可以与它在sa中的前一个字符取等号。每次有一个取了等的,值不同的字符就减少一个,设有i个取等,剩下的都不取等,则方案数为\(C(m,i)*C(k,n-i)\),其中前者是选i个取等的方案数,后者是给n-i个字符赋不同值的方案数。

总方案数为\(\sum_{i=0}^{m}C(m,i)*C(k,n-i)\),到这里已经可以算了。

我们再优化一下,由于\(m\leq n\),当\(i>m\)\(C(m,i)=0\),因此式子可以用范德蒙卷积优化,写为\(\sum_{i=0}^{n}C(m,i)*C(k,n-i)=C(m+k,n)\)

看看代码。

Ac_code

#include<bits/stdc++.h>

using namespace std;

const int N = 4e5 + 10,mod = 998244353;

template<int T>
struct ModInt {
    const static int mod = T;
    int x;
    ModInt(int x = 0) : x(x % mod) {}
    int val() { return x; }
    ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); }
    ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
    ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
    void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;}
    void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;}
    void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
    void operator /= (const ModInt &a) { *this = *this / a; }
    friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
    
    ModInt pow(int n) const {
        ModInt res(1), mul(x);
        while(n){
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        if (u < 0) u += mod;
        return u;
    }
    
};
typedef ModInt<mod> mint;

mint fact[N],infact[N];

void init()
{
    fact[0] = infact[0] = 1;
    for(int i=1;i<N;i++) fact[i] = fact[i-1]*mint(i);
    infact[N-1] = fact[N-1].inv();
    for(int i=N-2;i;i--) infact[i] = infact[i+1]*mint(i+1);
}

mint C(int a,int b)
{
    if(a<b) return 0;
    return fact[a]*infact[b]*infact[a-b];
}

int main()
{
    int n,k;cin>>n>>k;
    init();
    vector<int> sa(n+1),rk(n+1);rk[n+1] = 0;
    for(int i=1;i<=n;i++) cin>>sa[i],rk[++sa[i]]=i;   
    int m = 0;
    for(int i=2;i<=n;i++) m += rk[sa[i]+1]>rk[sa[i-1]+1];
    cout<<C(m+k,n)<<'\n';
    return 0;
}
posted @ 2022-09-17 10:13  艾特玖  阅读(34)  评论(0)    收藏  举报