明日缥缈
[集训队作业2018]count
显然等价于数本质不同的笛卡尔树的个数,而当 \(m\leq n\) 时这等价于最长左链的边数+1小于等于 \(m\) 。
注意到我们可以用孩子兄弟表示法表示一棵任意有根树,同理,我们可以把一棵 \(n\) 个点的二叉树唯一确定到一棵 \(n+1\) 个点的有根树,且最长左链的长度就是最大深度。因此就是问有多少 \(n+1\) 个点的有根树,使得最大深度 \(\leq m\) 。考虑求出树的括号序,那么把左括号看成+1,右括号看成-1,等价于从 \((0,0)\) 走到 \((2n,0)\),每次向右上或者右下走一步,且任意时刻位于 \([0,m]\) 之间的路径条数。
这是一种经典的双折线容斥,详见 JLOI 骗我呢 。
记 $$path(n,m)=\binom{n}{\frac{n+m}{2}}$$ 表示 \((0,0)\) 走到 \((n,m)\) 不考虑限制的方案数。
如果经过直线 \(y=-1\),我们记录一个 A,如果经过一个 \(y=m\),我们记一个 B 。
那么考虑不合法路径对应的字符串,我们把相邻形同的缩成一个之后,一定形如 ABABA\(\dots\),或者 BABAB\(\dots\) 。
我们用总的方案数,减掉 \(A\) 开头的,减掉 \(B\) 开头的,就是答案。
仿照卡特兰数的求法,我们把原点关于 \(A\) 对称,得到点 \(P\),那么每一条\(A\) 开头的路径关于第一个交点对称之后一定变成从 \(P\) 出发的。但是从 \(P\) 出发的路径不一定都是以 \(A\) 开头的,有可能是以 \(BA\) 开头的。
我们减掉以 \(BA\) 开头的就行了,类似这样的思路,我们不断迭代,进行容斥就好了。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
const int mod = 998244353;
int Pow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
int fac[N],ifac[N];
void init(int n)
{
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=Pow(fac[n],mod-2);
for(int i=n-1;i>=0;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
inline int binom(int n,int m)
{
if(n<0||m<0||n<m) return 0;
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int path(int a,int b,int c,int d)
{
int n=c-a,m=d-b;
if((n+m)%2==1)return 0;
return binom(n,(n+m)/2);
}
int n,m;
int main()
{
init(N-1);
cin>>n>>m;
if(m>n)
{
cout<<0;
return 0;
}
n*=2;
int l=-1,r=m+1;
int ans=path(0,0,n,0),c=mod-1,p1=2*l,p2=2*r;
while(abs(p1)<=n)
{
ans=(ans+1ll*c*path(0,p1,n,0)%mod)%mod;
if(c==1)p1=2*l-p1;else p1=2*r-p1;
c=mod-c;
}
c=mod-1;
while(abs(p2)<=n)
{
ans=(ans+1ll*c*path(0,p2,n,0)%mod)%mod;
if(c==1)p2=2*r-p2;else p2=2*l-p2;
c=mod-c;
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号