1. 程式人生 > 其它 >[數學記錄]P1232 樹的計數

[數學記錄]P1232 樹的計數

題意:

給出一棵樹的 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);
}