2020-10-31 NOIP模拟T1

题意

已知一个二叉搜索树,求依次插入而形成这颗树的序列数量

思路

首先建树是显然的(dbq我一开始想了很久这树咋建)
二叉搜索树与别的普通的树不同,发现每个节点的位置其实只与它的父节点(看从哪个角度看吧,也可以说是子节点)有关,所以每次建树只需要记录它的左儿子和右儿子然后按照二叉搜索树的性质来建树就可以把这棵树建完了。
然后我们继续观察这棵树,发现对于这个已知的树,每棵子树(包括整棵树)的根节点是确定的,然而每棵子树(不包括整棵树)的根节点又与它自己的父节点相关,于是我们就可以联想到树形 D P DP DP了。
对于这种子节点的选择要依赖于根节点的题一般都可以往树形 D P DP DP上想
然后又发现对于一棵子树之内的节点,我们怎么在序列里摆放它们并没有什么影响。
注意子树内部节点其实是有序的,只是想表达对于根节点相同只是分别为左右子树的两棵树,设左右子树子节点为 a , b a,b a,b,那么 a , b a,b a,b在序列里出现的顺序其实是互不影响的,它只要在根节点之后被摆放那么形成的子树形态一定是一样的。
比如根节点是 7 7 7,第一棵子树是 6 , 5 6,5 6,5,第二棵是 8 8 8,那么 7 , 6 , 5 , 8 7,6,5,8 7,6,5,8 7 , 8 , 6 , 5 7,8,6,5 7,8,6,5是一样的。
于是这样就和子树的大小(对根节点为 u u u的子树设为 s z u sz_u szu)与子树内部节点的顺序扯上关系了。设左子树为 l s ls ls,右子树为 r s rs rs,那在根节点 u u u确定的情况下, u u u后面与 u u u子树有关的序列一共 s z l s + s z r s sz_{ls}+sz_{rs} szls+szrs个空位,我们要在这些空位里插入 s z l s sz_{ls} szls,设对于一棵树 u u u它方案数为 f u f_u fu,那么有如下转移:
f u = f l s ∗ f r s ∗ ( s z l s + s z r s s z l s ) f_u=f_{ls}*f_{rs}*\binom{sz_{ls}+sz_{rs}}{sz_{ls}} fu=flsfrs(szlsszls+szrs)
关于组合数意义以及这个式子
关于组合数的一些性质
一开始的疑惑是组合数里明明没有顺序是怎么维护子树对应顺序的,考虑组合数的意义是用全排列除以重复的个数,那其实获得的数比排列数要少,相当于把顺序都搞没了,于是它与排列的区别就是:组合数就是选出来的元地位平等,排列数则不平等(组合数限制更少)。
组合数就是单纯选数,相当于排列把顺序给它去掉了,在这道题里就是选位置去掉位置间相对顺序,然后乘以子树顺序 f u f_u fu相当于就又有顺序了。选位置就只是钦定个数然后顺序由子树 f u f_u fu决定。

关于逆元
一开始写法线性推逆元是求出的每个数的逆元并不是阶乘的逆元,要求阶乘逆元要换一种写法(调了一上午)。

其他一些注意点
建树的时候小于根节点直接就往左子节点,不需要和右子节点比大小判断是否往右边建树(对二叉查找树性质还是不熟悉)
对于叶子节点一定有 f = 1 , s z = 1 f=1,sz=1 f=1,sz=1,在递归到末尾的时候赋值,避免最后相乘全成为 0 0 0

代码
注释的代码对于这道题是错误的写法,用那种写法如果要改对的话数组都开不下。

#include <bits/stdc++.h>
#define ls(x) lson[x]
#define rs(x) rson[x]
#define fac c
#define ifac inv
#define mod Mod 
using namespace std;
const int Mod=1e9+7;
const int N=1005;
int c[N],f[N],inv[N],n,rt,lson[N],rson[N],sz[N],m,a[N];
inline int mul(int a,int b){
	return (long long)(a%Mod)*(b%Mod)%Mod;
}
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
int qpw(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}
inline void prework(){
//	c[0]=1;inv[0]=inv[1]=1;
//	for(int i=1;i<=N;++i) c[i]=mul(c[i-1],i);
//	for(int i=2;i<=N;++i) inv[i]=mul(Mod-Mod/i,inv[Mod%i])%Mod;
	fac[0]=1;
	for(int i=1;i<=N-3;++i) fac[i]=mul(fac[i-1],i);
	ifac[N-3]=qpw(fac[N-3],mod-2);
	for(int i=N-4;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
}
void build(int u,int rt){
	if(u<rt){
		if(!ls(rt)) {ls(rt)=u;return;}
		else build(u,ls(rt));
	}
	else{
		if(!rs(rt)) {rs(rt)=u;return;}
		else build(u,rs(rt));
	}
}
inline int C(int n,int m){
	return mul(c[n],mul(inv[m],inv[n-m]));
}
void dfs(int u){
	f[u]=1;sz[u]=1;
	if(ls(u)) {dfs(ls(u));}
	else f[ls(u)]=1;
	if(rs(u)) {dfs(rs(u));}
	else f[rs(u)]=1;
	sz[u]+=sz[ls(u)]+sz[rs(u)];
	f[u]=mul(mul(f[ls(u)],f[rs(u)]),C(sz[u]-1,sz[ls(u)]));
	return;
}
signed main(){
//	freopen("bst.in","r",stdin);
//	freopen("bst.out","w",stdout);
	ios::sync_with_stdio(false);
	cout.tie(0);
	m=read();prework();
	while(m--){
		n=read();
		memset(f,0,sizeof(f));memset(sz,0,sizeof(sz));
		memset(lson,0,sizeof(lson));memset(rson,0,sizeof(rson));
		rt=a[1]=read();
		for(int i=2;i<=n;++i)
			a[i]=read(),build(a[i],rt);
		dfs(rt);
		cout<<f[rt]-1<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值