• [集训队作业2018] count


    一、题目

    点此看题

    二、解法

    首先转化题意:长度为 \(n\) 个点的序列,值域为 \([1,m]\),要求 \(1,2...m\) 都出现,问本质不同的笛卡尔树数量。

    有一个关键的 \(\tt observation\) 是:如果在一个排列中 \(1,2...m\) 中的某数未出现,一定可以构造出另一个 \(1,2...m\) 都出现过的排列,使得它们的笛卡尔树同构。这说明 \(1,2...m\) 都出现的限制是可以直接拿掉的,极大地简化了问题。

    考虑在值域 \([1,m]\) 的限制下,合法的笛卡尔树需要满足什么条件。一个必要条件是:从每个点一直往左走的步数都不超过 \(m-1\)。充分性是易得的,因为我们构造笛卡尔树时,一定是先往根上放 \(m\),递归下去时,左边是无法使用 \(m\) 的,但是右边还可以使用 \(m\)(这种求本质不同方案的问题,是类似子序列自动机的贪心匹配思想的)

    一个神仙的操作是二叉树转多叉树,首先我们新建一个虚根,把原树的根设置为虚根的儿子。然后我们递归地构造,如果某个点 \(v\)\(u\) 的右儿子,我们把 \((u,v)\) 的边断开,改接上 \((fa[u],v)\) 的边。

    因为本质不同的二叉树数量等价于本质不同的先序遍历数量,而二叉树和多叉树的先序遍历是相同的,所以我们只需要对多叉树的本质不同先序遍历计数即可。

    那么要求就转化成了多叉树的深度不超过 \(m\) 的先序遍历数量,也就是一个 \(2n\) 的合法括号序列,要求任意时刻 ( 的数量减去 ) 的数量不超过 \(m\);又可以转化成在网格图上行走,从 \((0,0)\) 走到 \((n,n)\),要求不能碰到 \(y=x+1\)\(y=x-(m+1)\)

    显然这和 骗我呢 最后的计算方法是一样的,把终点按照两条直线一直翻折然后容斥,时间复杂度 \(O(n)\)

    #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);
    }
    
  • 相关阅读:
    DataGridView 鼠标双击获得行列索引
    浅谈MVC、MVP、MVVM架构模式的区别和联系
    Codeforces 336D Dima and Trap Graph 并查集
    Codeforces 601C Kleofáš and the n-thlon 概率dp
    Codeforces 311B Cats Transport 斜率优化dp
    Codeforces 908F New Year and Rainbow Roads
    Codeforces 12D Ball cdq分治
    Codeforces 291 E Tree-String Problem AC自动机
    Codeforces 932E Team Work 数学
    Codeforces 463E Caisa and Tree
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16379754.html
Copyright © 2020-2023  润新知