51Nod1802 左偏树计数

题目大意

求$n$个点的无标号左偏树个数

既然你都点进来了,那么估计也是奔着题解来的....

废话少说....

首先,左偏树有这么一些性质

设最右链长度为$r[p]$

1.左偏树的子树仍然是左偏树

2. $r[p] = r[rs[p]] + 1$

3. $r[p] \leqslant \log_2 (p + 1)$

因此,考虑设状态$dp[i][j]$表示$i$个点构成右链长度为$j$的方案数

由于右儿子的右链长度一定为$j - 1$

只要枚举左儿子右链长度和左儿子子树大小就能转移

但是这样子复杂度会爆炸

因此考虑剪枝

首先,右链长度为$j$代表着其子树大小一定大于等于$2^j - 1$

然后,左儿子右链长度一定大于等于$j - 1$

然后就可以AC了

反正随意调一调发现过了样例然后就一A了.......

其实还有可以优化的地方,但是人太懒了....

#include <cstdio>
#include <iostream>
using namespace std;

#define sid 1005
#define ri register int

int n, p;
int bit[sid], lg2[sid];
int f[sid][15];

int main() {
    cin >> n >> p;
    for(ri i = 0; i <= 25; i ++) bit[i] = 1 << i;
    for(ri i = 2; i <= n + 1; i ++) lg2[i] = lg2[i >> 1] + 1;
    
    f[0][0] = 1; f[1][1] = 1;
    for(ri i = 2; i <= n; i ++)
    for(ri j = 1; j <= lg2[i + 1]; j ++)
    for(ri lp = j - 1; bit[lp] + bit[j - 1] - 2 <= i; lp ++)
    for(ri L = bit[lp] - 1; ; L ++) {
        if(L > i) break;
        if(i - L - 1 < bit[j - 1] - 1) break;
        f[i][j] = (f[i][j] + 1ll * f[L][lp] * f[i - L - 1][j - 1] % p) % p;
    }
    
    int ans = 0;
    for(ri i = 0; i <= lg2[n + 1]; i ++)
    ans = (ans + f[n][i]) % p;
    printf("%d\n", ans); 
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/reverymoon/p/9471427.html
今日推荐