1. 程式人生 > 其它 >題解:玩具

題解:玩具

  概率期望DP,其DP性質發現於隨n的遞變,問題空間呈規律性變化

通常情況下,期望問題有兩種基本解決思路:1.計算出總貢獻除以總情況數

2.計算出子問題概率,利用定義,再乘以子問題對應貢獻即可

  首先想到思路一,設計f[i][j]表示進行到第i輪,子樹最大深度為j的情況數

考慮如何轉移,想到的是將第i次操作在葉節點進行,由i-1進行拓展,發現

需要維護當前階段第j層葉節點數,O(n^3)可行,然而發現每一層節點都由上

一層節點進行轉移,時間複雜度O(n^4),(當然,矩陣優化O(n^3*logn)卡常

可做,如果不嫌麻煩),於是當事一直思考如何優化,事實上這是錯誤的策略

通常情況下我們並不能一下子想到正確DP,此時應考慮更換思路

  事實上思路一還有一種做法,轉化為計數DP即可(這也是第一種思路的本質)

考慮給樹定形,發現1,2節點的位置相對固定,而樹形DP本質問題是子樹與

父樹的相互關係,在1,2節點固定後,我們並不需要考慮每一種樹形態,問題

在於樹的期望深度,那麼DP過程只需要轉移子樹深度,大小,分別考慮以1,2

節點為根,乘法計數,即可O(n^4),這種思考方式的有點在於並不去考慮與問題無

關的樹形,而是將所有樹形化歸為一種抽象形態,避免了具體考慮轉移的繁雜,

個人認為相較於第一種方法更優

  上述兩種方式均以計數DP方式計算總貢獻進而推出期望,但是其並不適用與

本問題,本問題更接近與抽象問題,因為在階段推進過程中,僅僅是生成樹,樹

的形態不定,也就是沒有統一的轉移方式,具體思考會陷入陷阱。

  於是我們考慮抽象思考,繞過樹形態的變化,採取第二種思路,巨集觀考慮從

一種形態轉化為另一種形態的概率。轉移無定式,對於本題更是如此,考慮對於

當前樹,他是如何被生成的。因為對於生成方式完全無描述,我們可以認為,第

一種情況是有子樹在葉節點拓展生成,另一種情況是由新節點連線若干子樹行形成。

  發現第二種情況顯然比第一種情況簡潔,於是對於i個節點,j深度子樹生成概率

就由i-1節點,最大深度等於j-1的森林生成概率轉移而來,於是可以並行DP

(很多情況下,當一中DP無法進行時,可以利用多DP並行,記錄所需資訊來輔助

主DP轉移),發現對於森林概率的DP,其最優子結構由子森林與子樹構成,也就

是說森林與樹生成概率的轉移相輔相成,基本完成。

  當然存在一個問題為森林的轉移存在概率限制,即另需記錄i節點j深度森林中

某棵樹為k節點概率,由於這棵樹的編號是等效的,故數學歸納得概率為1/i

程式碼如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 const I MAXN = 205;
 5 I n,mod,res;
 6 I f[MAXN][MAXN],g[MAXN][MAXN],d[MAXN];
 7 inline I qpow (I base,I index) {
 8     I res(1);
 9     for ( ; index ;index >>= 1,base = 1ll * base * base % mod)
10       if (index & 1) res = 1ll * res * base % mod;
11     return res;
12 }
13 signed main () {
14     cin >> n >> mod;
15     for (I i(1);i <= n; ++ i) d[i] = qpow (i,mod - 2);
16     for (I i(0);i <= n; ++ i) f[1][i] = g[1][i] = g[0][i] = 1;
17     for (I i(2);i <= n; ++ i)
18       for (I j(0);j <= n; ++ j) {
19         if (j) f[i][j] = g[i - 1][j - 1];
20         for (I k(1);k <= i; ++ k)
21           g[i][j] = (g[i][j] + 1ll * f[k][j] * g[i - k][j] % mod * d[i]) % mod;
22       }
23     for (I i(1);i <= n; ++ i)
24       res = (res + 1ll * (f[n][i] - f[n][i - 1]) * i) % mod;
25     cout << (res + mod) % mod << endl;  
26 }
View Code