题目描述
不过只用求一项。
\(n,k\leq {10}^7\)
题解
不难发现一棵 \(n\) 个叶子的树唯一对应了一个长度为 \(2n-2\) 的括号序列。
往左儿子走对应加一个左括号,退到上一个只有左儿子的点,往右儿子走对应加一个右括号,那么合法的树一定满足:每一个前缀中左括号数量 \(-\) 右括号数量 \(<m-1\)。
求括号序列个数有一种容斥做法:把左括号视为 \(1\),把右括号视为 \(-1\)。用总方案数减掉走到 \(-1\) 的方案数
每一个走到 \(-1\) 的方案在走第一次走到 \(-1\) 后把后面的括号取反,对应一个结尾走到 \(-2\) 的方案数。
这道题要求不能走到 \(m-1\),那么还要减掉结尾走到 \(2m-2\) 的方案数。
但是既走到 \(-1\) 又走到到 \(2m-2\) 的方案会被减掉两次,所以要加上结尾走到 \(2m\) 的方案数和结尾走到 \(-2m\) 的方案数。
然后又要减掉 \(0\to -1\to m-1\to -1\) 的方案数和 \(0\to m-1\to -1\to m-1\) 的方案数。
以此类推。
时间复杂度:\(O(n)\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll p=998244353;
const int N=10000010;
int n,m;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
int ifac[2*N];
ll binom(int x,int y)
{
return x>=y&&y>=0?(ll)ifac[y]*ifac[x-y]%p:0;
}
int main()
{
open("c");
scanf("%d%d",&n,&m);
n--;
ll s=1;
for(int i=1;i<=2*n;i++)
s=s*i%p;
ifac[2*n]=fp(s,p-2);
for(int i=2*n;i>=1;i--)
ifac[i-1]=(ll)ifac[i]*i%p;
ll ans=binom(2*n,n);
for(int i=0;i<n;i+=m)
{
ans-=binom(2*n,n+i+1);
ans-=binom(2*n,n+i+m-1);
ans+=2*binom(2*n,n+i+m);
}
ans=(ans%p*s%p+p)%p;
printf("%lld\n",ans);
return 0;
}