count
对于一个长度为 \(n\) 的整数序列 \(A\),若其满足下列条件,则称其为极好序列:
-
对于任意 \(i \in [1,n]\),\(a_i \in [1,m]\)。
-
对于任意 \(x \in [1,m]\),存在 \(i \in [1,n]\) 使得 \(a_i = x\)。
记 \(f(A:[l,r])\) 为子区间 \([l,r]\) 最大值的下标,若有多个最大值取最小的下标。
定义极好序列 \(A_1\) 和 \(A_2\) 本质相同,当且仅当其满足如下条件:
- 对于任意 \([l,r]\),满足 \(f(A_1:[l,r]) = f(A_2:[l,r])\)。
求本质不同的极好序列个数,对 \(998244353\) 取模。
\(n < m\) 时答案为 \(0\),下面默认 \(n \geq m\)。
对于最大值,我们考虑使用笛卡尔树来刻画。
显然,\(A_1\) 和 \(A_2\) 所有区间的最大值下标相同,等价于两个序列的笛卡尔树相同。
也就是说,我们只需要对笛卡尔树计数即可。
笛卡尔树对于所有节点 \(u\),需要有 \(a_u > a_{lc_u}\) 且 \(a_u \geq a_{rc_u}\)。
首先我们尝试找到一个笛卡尔树合法的必要条件。
从 \(a_u > a_{lc_u}\) 出发,这是唯一能使值减小的方法。
从 \(m\) 到 \(1\),值最多减少 \(m-1\) 次,不妨记 \(u\) 到 \(lc_u\) 的边为有效边。
也就是说,任意一个点到根的路径上,最多只有 \(m-1\) 条有效边。
容易发现这也是一个充分条件,构造是容易的。
接下来对笛卡尔树计数,我们发现笛卡尔树比较难以刻画。
联想到左儿子右兄弟的表示方法,考虑反着应用。
笛卡尔树上的有效边,在当前表示方法中,意味着新增一个儿子节点。
而一条链上的有效边不超过 \(m-1\) 条,意味着新的树的深度不超过 \(m\)。
也就是说,笛卡尔树可以看作一棵深度不超过 \(m\) 的多叉树。
由于笛卡尔树的根节点存在右儿子,所以多叉树需要补充一个根节点。
定义根节点深度为 \(0\),我们需要对 \(n+1\) 个点,每个点的深度 \(\leq m\) 的树计数。
考虑用括号序列描述树,显然多叉树可以和合法括号序列构成双射。
考虑先去除根节点,这样原题就可以看作对长度为 \(2n\) 的括号序列计数,使得:
- 对于每个前缀 \([1,i]\),左括号与右括号的个数差 \(\in [0,m]\)。
我们不妨将其看成格路计数,则我们需要做的就是从 \((0,0)\) 走到 \((n,n)\)。
每一步可以向上或向右走,要求不经过 \(y = x - 1\) 和 \(y = x + m + 1\)。
我们考虑进行容斥。所有走法的总数应该是 \(\dbinom{2n}{n}\),接下来我们需要减去不合法的方案。
我们不妨记第一条线为 \(A\),第二条线为 \(B\),将不合法的方案经过的线记录下来。
我们发现其必然是一个 \(A\) 和 \(B\) 构成的字符串,不妨将连续段合并成一个字符。
使用经典的翻折路径方法即可解决问题,复杂度 \(O(n)\)。
#include<iostream>
#include<cstdio>
using namespace std;
const long long mod=998244353;
const int N=200000;
long long fact[N+10],invfact[N+10];
long long inv(long long num){
long long pre=mod-2,ans=1;
while(pre){
if(pre&1){
ans=ans*num%mod;
}
num=num*num%mod;
pre>>=1;
}
return ans;
}
void init(){
fact[0]=invfact[0]=1;
for(int i=1;i<=N;i++){
fact[i]=fact[i-1]*i%mod;
invfact[i]=invfact[i-1]*fact[i]%mod;
}
long long tmp=inv(invfact[N]);
for(int i=N;i>=1;i--){
invfact[i]=invfact[i-1]*tmp%mod;
tmp=tmp*fact[i]%mod;
}
}
int n,m;
long long C(int num){
return fact[2*n]*invfact[num]%mod*invfact[2*n-num]%mod;
}
long long solve1(){
long long ans=0;
int pre=0;
bool flag=false;
while(true){
if(flag==false){
pre=-1-pre;
}
else{
pre=m+1-pre;
}
if(pre<-n || pre>n){
break;
}
if(flag==false){
ans+=C(pre+n);
}
else{
ans-=C(pre+n);
}
ans%=mod;
flag=!flag;
}
return ans;
}
long long solve2(){
long long ans=0;
int pre=0;
bool flag=false;
while(true){
if(flag==true){
pre=-1-pre;
}
else{
pre=m+1-pre;
}
if(pre<-n || pre>n){
break;
}
if(flag==false){
ans+=C(pre+n);
}
else{
ans-=C(pre+n);
}
ans%=mod;
flag=!flag;
}
return ans;
}
int main(){
init();
scanf("%d %d",&n,&m);
if(m>n){
printf("0");
return 0;
}
long long ans=C(n);
ans-=solve1();
ans=(ans%mod+mod)%mod;
ans-=solve2();
ans=(ans%mod+mod)%mod;
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号