51nod1802 左偏樹計數
阿新 • • 發佈:2018-08-14
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 左偏樹計數