CF1237E 【Balanced Binary Search Trees】

神仙题c++

\(CF1237E ~Balanced~ Binary ~Search ~Trees\)spa

题意:设计

须要求出有多少棵点数为\(n(n\le 10^6)\)点权为\({1,2,...,n}\)的二叉搜索树知足:code

\(1):\) 除了最下面一层外,全部层都是满的;get

\(2):\) 对于每一个点,若是它有左儿子,那么左儿子的点权和它的奇偶性不一样;若是它有右儿子,那么右儿子的点权和它的奇偶性相同。it

答案对\(998244353\)取模class


\(\rm Sol:\)二叉树

能够发现定义\(1\)即这棵树是一个满二叉树,因而对于根节点,有除去其以后仍然有其为一颗满二叉树搜索

设知足定义\(2\)的满二叉树为完美树,则能够发现因为原树为一棵二叉查找树,因而其最右边的点必定为\(n\),因此有根的奇偶性与\(n\)的奇偶性相同遍历

不难看出两个性质:

\(1):\) 一棵完美树的子树仍然是完美树

\(2):\) 因为权值为\(1-n\),因此这棵二叉搜索树的中序遍历为\(1-n\),每棵子树则能够对应成为一个区间

考虑假设某一个数\(x\)做为根,此时有\(x\)的奇偶性与\(n\)相同,则其左子树的区间能够表示为\([1,x-1]\),右子树区间则能够表示为\([x+1,n]\),且有左子树大小为\(x-1\),右子树大小为\(n-x\),由于\(n\)的奇偶性和\(x\)相同,因此\(n-x\)必然为偶数,且\(x-1\)的奇偶性与\(x\)相反,因为左子树仍然是一棵完美树,因此再使用上述结论能够获得:

一颗完美树知足其左子树根的奇偶性与子树大小相同,而右子树大小为偶数

接下来因为二叉搜索树只关心相对的大小关系且其某一个子树能够被表示成为一个区间\([l,r]\),因此咱们使用\([1,r-l+1]\)对应替换此树全部节点对于答案没有影响,容易发现假设其原先为一棵完美树则替换后仍然是一颗完美树

因此问题与原树对应的区间编号无关而之和此树大小有关

接下来考虑将两颗完美树合并成为一颗大完美树以及其合法性,按照前面的条件咱们能够获得合并以后的树为完美树当且仅当:\(1.\)合并以后知足原树为满二叉树,\(2.\)右子树大小为偶数

咱们能够手玩获得:大小为\(1,2,3,4,5\)的完美树形态以下:

\(size=1:\quad 1\)

\(size=2:\quad 2.left\to 1\)

\(size=3:\) 不存在合法

\(size=4:\)为样例

\(size=5:\)仅存在一个,为:

\(3.left\to 2,2.left\to1,3.right\to 5,5.left \to 4\)

能够观察到除去\(size=1\)以外的全部完美树高度均\(>1\)且不为满二叉树

因为 性质\(1\) 咱们知道对于一个大小\(>5\)的完美树,有其最底层仍然是一棵完美树,换而言之其除去根以后必然是不满的,因此咱们能够得出一个可怕的结论:

将两颗完美树合并成一棵大小\(>5\)的完美树当且仅当\(1.):\) 其左右子树为完美树且高度相同,\(2):\)右子树大小为偶数

如今咱们能够设计一个很是粗略的\(dp\)了,令\(dp_{i,j}\)表示大小为\(i\)的完美树高度为\(j\)的时候的方案数,而后利用这个东西转移,这样是\(O(n\log n)\)的作法

仔细思考会发现一个更可怕的东西

咱们知道前\(5\)项的高度和方案数(前为高度,后为方案数)大体如此:

\((1,1),(2,1),(-,-),(3,1),(3,1)\)

注意到右子树的大小仅能是\(2\)\(4\)

对于右子树\(2\)而言惟一的合并是左\(2\)\(2\),合并获得\(5\)

对于右子树\(4\)而言惟一的合并是左\(4/5\)\(4\),合并获得大小为\(9/10\)的完美树,高度为\(4\)

那么这样对于大小为\(6-8\)的树其均不具备完美树,因而接下来能够用的大小仅有\(9/10\)

相似合并能够发现\(9/10\)仅能经过合并获得\(20/21\),而后可行的为\(41/42\)...能够发现合法的树均只有\(1\)

因而只须要拿\(4/5\)做为初值递推下去便可,复杂度\(O(\log n)\)

时间\(3s\)\(n\)只有\(10^6\)应该是为了放其余大常数非正解作法过....

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
int gi() {
    char cc = getchar() ; int cn = 0, flus = 1 ;
    while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    return cn * flus ;
}
int n ; 
signed main()
{
    n = gi() ; 
    if( n == 1 || n == 2 ) puts("1") ;
    else {
        int x = 4, y = 5 ; 
        while( max( x, y ) < n ) {
            int ux = x, uy = y ;
            if( ux & 1 ) x = ux + uy + 1, y = uy + uy + 1 ;
            else x = ux + uy + 1, y = ux + ux + 1 ; 
            if( x > y ) swap( x, y ) ;
        }
        if( x == n || y == n ) puts("1") ;
        else puts("0") ;
    }
    return 0 ;
}
相关文章
相关标签/搜索