1. 程式人生 > >51nod1802 左偏樹計數

51nod1802 左偏樹計數

name bit namespace family 轉移 std nod code 爆炸

題目大意

求$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; }

51nod1802 左偏樹計數