[集训队作业2018] count
一、题目
二、解法
首先转化题意:长度为 个点的序列,值域为 ,要求 都出现,问本质不同的笛卡尔树数量。
有一个关键的 是:如果在一个排列中 中的某数未出现,一定可以构造出另一个 都出现过的排列,使得它们的笛卡尔树同构。这说明 都出现的限制是可以直接拿掉的,极大地简化了问题。
考虑在值域 的限制下,合法的笛卡尔树需要满足什么条件。一个必要条件是:从每个点一直往左走的步数都不超过 。充分性是易得的,因为我们构造笛卡尔树时,一定是先往根上放 ,递归下去时,左边是无法使用 的,但是右边还可以使用 (这种求本质不同方案的问题,是类似子序列自动机的贪心匹配思想的)
一个神仙的操作是二叉树转多叉树,首先我们新建一个虚根,把原树的根设置为虚根的儿子。然后我们递归地构造,如果某个点 是 的右儿子,我们把 的边断开,改接上 的边。
因为本质不同的二叉树数量等价于本质不同的先序遍历数量,而二叉树和多叉树的先序遍历是相同的,所以我们只需要对多叉树的本质不同先序遍历计数即可。
那么要求就转化成了多叉树的深度不超过 的先序遍历数量,也就是一个 的合法括号序列,要求任意时刻 (
的数量减去 )
的数量不超过 ;又可以转化成在网格图上行走,从 走到 ,要求不能碰到 和
显然这和 骗我呢 最后的计算方法是一样的,把终点按照两条直线一直翻折然后容斥,时间复杂度
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 400005;
#define int long long
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,x,y,fac[M],inv[M];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
}
void flip1(int &x,int &y) {swap(x,y);x-=1;y+=1;}
void flip2(int &x,int &y) {swap(x,y);x+=(m+1);y-=(m+1);}
int C(int n,int m)
{
if(n<m || m<0) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int calc(int n,int m) {return C(n+m,n);}
signed main()
{
n=read();m=read();init(4e5);
if(m>n) {puts("0");return 0;}
int x=n,y=n,ans=calc(x,y);
while(x>=0 && y>=0)
{
flip1(x,y);ans-=calc(x,y);
flip2(x,y);ans+=calc(x,y);
}
x=y=n;
while(x>=0 && y>=0)
{
flip2(x,y);ans-=calc(x,y);
flip1(x,y);ans+=calc(x,y);
}
printf("%lld\n",(ans%MOD+MOD)%MOD);
}
分类:
数学-----容斥原理
, 数学-----计数原理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2021-06-15 CF1043G Speckled Band