每次 CF 都被中国场爆杀
今天模拟赛 MLE 了一题怒挂 80,又垫底了。。。
038 uoj#424. 【集训队作业2018】count
建出笛卡尔树,那么每个点到根节点的左儿子边数量不能超过 \(m\),因为向左走一步权值必须减一。
逆用兄弟儿子表示法,问题变为计数深度不超过 \(m\),节点数量为 \(n\),儿子有顺序的多叉树数量。
这里考虑生成函数并不是一个很好的选择,原因是深度实际上与儿子有顺序的多叉树括号序列有着较大的关系,我们不应将两维分开考虑。
考察其括号序列,左括号对应右上步,右括号对应右下步,即计数使用右上、右下步,从 \((0,0)\) 走到 \((2n,0)\),不越过 \(y=0\) 与 \(y=m\) 的格路数量。
两条直线的限制的格路行走是经典问题,反射容斥即可解决。
#include<stdio.h>
const int maxn=200005,mod=998244353;
int n,m,ans;
int fac[maxn],inv[maxn],nfac[maxn];
inline int C(int a,int b){
return a<b||b<0? 0:1ll*fac[a]*nfac[b]%mod*nfac[a-b]%mod;
}
int calc(int y){//(0,0) (2n,y)
return C(n+n,n+y/2);
}
int main(){
fac[0]=fac[1]=nfac[0]=nfac[1]=inv[1]=1;
for(int i=2;i<maxn;i++)
fac[i]=1ll*fac[i-1]*i%mod,inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod,nfac[i]=1ll*nfac[i-1]*inv[i]%mod;
scanf("%d%d",&n,&m);
if(n<m){
puts("0");
return 0;
}
ans=calc(0);
for(int y=0;y>=-n-n&&y<=n+n;){
y=-2-y,ans=(ans-calc(y)+mod)%mod;
y=2*(m+1)-y,ans=(ans+calc(y))%mod;
}
for(int y=0;y>=-n-n&&y<=n+n;){
y=2*(m+1)-y,ans=(ans-calc(y)+mod)%mod;
y=-2-y,ans=(ans+calc(y))%mod;
}
printf("%d\n",ans);
return 0;
}