[數學記錄]P1232 樹的計數
阿新 • • 發佈:2022-12-11
題意:
給出一棵樹的 dfs 序和 bfs 序,求所有可能的原樹的高度平均值
\(n \leq 2 \cdot 10^5\)
首先把 bfs 序變成 \(1 \to n\),這樣就需要把原樹上的節點分成若干層。
設 \(pos_{dfn_i}=i\),\(dfn\) 陣列存點性質,\(pos\) 陣列存點編號。
注意到每個節點是否分層對答案的貢獻獨立,單獨求出每個節點是否能分層即可。
首先處理分層對 dfs 序的限制:當 \(dfn_x > dfn_{x+1}\) 時,\(x\) 與 \(x+1\) 間必須分層。
現在研究一棵樹的 dfs 序應該滿足什麼條件。
思考做 dfs 的過程,每一步可以回溯或進入一個子節點。
借張圖,圖源 link
dfs 的每一步可以回溯或往下走到子節點。因為有換層出現,\(3\) 中的換層已經被計算,現在要把 \(3\) 篩選出來。
第一種情況 \(pos_y = pos_x+1\),第二種情況 \(pos_y < pox_x\),所以當 \(pos_y>pos_x+1\) 時一定屬於第三種情況。
因此獲得了兩個限制:\(dfn_x > dfn_{x+1}\) 時 \(x\) 必須分層,\(pos_y>pos_x+1\) 時 \(x\) 與 \(y\) 之間不能再分層。
#include <cstdio> using namespace std; const int M = 2e5 + 5; int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();} return x * f; } int n, bfs[M], pos[M], dfs[M], dif[M], dfn[M]; void solve(int l, int r) {++dif[l]; --dif[r+1];} int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) dfn[read()] = i; for(int i = 1; i <= n; i++) pos[dfn[read()]] = i; for(int i = 1; i <= n; i++) dfn[pos[i]] = i; solve(1, 1); double ans = 1; for(int i = 1; i < n; i++) { if(pos[i] < pos[i+1] - 1) solve(pos[i], pos[i+1] - 1); if(dfn[i] > dfn[i+1]) ++ans, solve(i, i); } int t = 0; for(int i = 1; i < n; i++) { t += dif[i]; ans += t ? 0 : 0.5; } printf("%.3lf\n", ans+1); }