题目大意
求$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; }
原文地址:https://www.cnblogs.com/reverymoon/p/9471427.html
时间: 2024-10-01 19:58:22