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\),问题在于能不能取等。如果取等,则我们实际在比较i和j代表的后缀对比大小时,将会忽略相同的第一位,从第二位开始比较,因此取等的条件是\(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;
}

浙公网安备 33010602011771号