UOJ 122 NOI2013 樹的計數

2022-05-09 16:11:10 字數 4326 閱讀 3881

[uoj#122][noi2013]樹的計數

試題描述

我們知道一棵有根樹可以進行深度優先遍歷(dfs)以及廣度優先遍歷(bfs)來生成這棵樹的 dfs 序以及 bfs 序。兩棵不同的樹的 dfs 序有可能相同,並且它們的 bfs 序也有可能相同,例如下面兩棵樹的 dfs 序都是 1 2 4 5 3,bfs 序都是 1 2 3 4 5。

現給定乙個 dfs 序和 bfs 序,我們想要知道,符合條件的有根樹中,樹的高度的平均值。即,假如共有 k

'>k

棵不同的有根樹具有這組 dfs 序和 bfs 序,且他們的高度分別是 h1,

h2,…

,hk'>h1,h2,…,hk

,那麼請你輸出:h1

+h2+

⋯+hk

k'>(h1+h2+⋯+hk)/k

輸入

第一行包含 1

'>1

個正整數 n

'>n

,表示樹的節點個數。

第二行包含 n

'>n

個正整數,是乙個 1∼n

'>1∼n

的排列,表示樹的 dfs 序。

第三行包含 n

'>n

個正整數,是乙個 1∼n

'>1∼n

的排列,表示樹的 bfs 序。

輸入保證至少存在一棵樹符合給定的兩個序列。

輸出

僅包含 1

'>1

個實數,四捨五入保留恰好三位小數,表示樹高的平均值。

輸入示例

512

4531

2345

輸出示例

3.500
資料規模及約定100% 的測試資料,滿足:2≤n

≤200000

'>2≤n≤200000

2≤n

≤200000

'>題解

2≤n≤

200000

'>首先自己 yy 了乙個 90 分的做法。

我們給樹重標號,使得它的 dfs 序變成 1~n(bfs 序也隨之改變)。

那麼在這裡發現:如果 bfs 序中兩個數 i 和 i+1,i+1 的位置在 i 後面,並且 i 和 i+1 不相鄰,那麼意味著 i+1 一定是 i 的兒子(性質1),並且在 bfs 序中,處在 i 和 i+1 位置中間的節點要麼是 i 的兄弟並且在 i 的右邊,要麼是 i+1 的兄弟並且不是 i 的兒子並且在 i+1 的左邊(這裡的左右指的是樹上同一層節點的左右關係)。

然後,對於 bfs 序中相鄰兩個數 b[i] 和 b[i+1],如果 b[i] > b[i+1] 那麼,節點 b[i] 和 b[i+1] 一定在不同層,更確切地,b[i+1] 在 b[i] 的下一層(因為會先 dfs 到 b[i] 這個節點)。(性質2

注意到如果我們給每個節點確定了深度,樹的形態一定確定了。

給個例子:

dfs 序:1 2 3 4 5 6 7 8 9

bfs 序:1 2 6 7 3 4 5 8 9

那麼發現 (2, 3) 和 (7, 8) 這兩對數滿足性質1,(b[4]=7, b[5]=3) 這對數滿足性質2

那麼我們怎麼給它們安排深度呢?注意到深度按照 bfs 序排列從左往右是不下降的。

節點 1 深度一定是 1,節點 2 深度一定為 2;由 (2, 3) 和 (b[4]=7, b[5]=3) 這兩個條件知道節點 6 和 7 深度都是 2,節點 3 的深度為 3;由 (7, 8) 得知節點 8 深度為 3,那麼節點 4 和 5 的深度也就自然都是 3 了;節點 9 的深度無所謂,可以是 3 或者 4。

深度序列:1 2 2 2 3 3 3 3 (3 or 4)

差分一下這個序列,即 dep[i] = dep[i+1] - dep[i](捨去最後乙個位置的數),得到:1 0 0 1 0 0 0 (0 or 1)

於是問題變成了,在這個差分後的新序列中,給每個位置上確定 0 或 1,有些位置上必須是 1,然後有一些區間,每個區間內的 1 的個數不能多於 1,求期望有多少個 1。

在這個例子中,必須填 1 的位置是 1 和 4,區間為 [2, 4] 和 [4, 7]。

那麼就可以 dp 了。f[i] 表示考慮前 i 個位置,並且第 i 位填 1 的方案數;g[i] 表示考慮前 i 個位置,第 i 為填 1 的所有方案中 1 的個數之和;那麼 f[i] 和 g[i] 都可以從【上乙個必填 1 的位置】到【包含位置 i 的所有區間中最小的左端點 - 1】這一段區間中轉移過來,線段樹維護即可。如果 f 和 g 都用 long double,可以蹭到 90 分。

#include #include #include #include #include #include #include #include using namespace std;

int read()

while(isdigit(c))

return x * f;

}#define maxn 200010

#define double long double

#define pdd pair int n, dfn[maxn], bfn[maxn], id[maxn], pos[maxn];

bool tag[maxn];

vector add[maxn], del[maxn];

priority_queue q, delq;

int lft[maxn];

void addi(int l, int r)

double sf[maxn<<2], sumv[maxn<<2];

void update(int o, int l, int r, int x, double f, double v)

return ;

}pdd query(int o, int l, int r, int ql, int qr)

int main()

for(int j = 0; j < add[i].size(); j++) q.push(-add[i][j]);

if(q.empty()) lft[i] = i; else lft[i] = -q.top();

}// for(int i = 1; i <= n; i++) printf("%d%c", lft[i], i < n ? ' ' : '\n');

int lst = 1;

update(1, 1, n, 1, 1, 1);

for(int i = 2; i < n; i++)

double sumv = 0, sf = 0;

for(int i = n - 1; i; i--)

printf("%.3lf\n", (sumv + sf) / sf);

return 0;

}

更簡單的做法就是讓 bfs 序變成 1~n 的排列,dfs 序隨之變化,然後分情況討論節點 i 和 i-1 必定不在同一層,可以在同一層也可以不在同一層,必定在同一層,貢獻分別是 1、0.5 和 0。

以下省略條件:對於節點 i 和節點 i-1。令 pos[i] 表示節點 i 在 dfs 序中的位置。

可以在同一層,也可以不在:pos[i] = pos[i-1] + 1,並且如果節點 i 放在了節點 i-1 那一層,則節點 i 是當前層的最靠右的節點,即 pos[1], pos[2], ... , pos[i-1] 標記上了 dfs 序中的開頭和結尾;

必定在同一層:其餘情況。

並查集判判就好了。

#include #include #include #include #include #include #include #include using namespace std;

int read()

while(isdigit(c))

return x * f;

}#define maxn 200010

int n, dfn[maxn], bfn[maxn], id[maxn], pos[maxn], fa[maxn], siz[maxn];

int findset(int x)

int main()

if(pos[i] < n) }

printf("%.3lf\n", sum);

return 0;

}

UOJ126 NOI2013 快餐店(基環樹dp)

題目鏈結 思路 現在圖中保證一定只有乙個環,這個基環樹,也就是說去掉環上的任意一條邊,它能形成一棵樹,先看最長路徑不在環上的情況,那麼最長路徑就是在環上的點為根的子樹中了,這個求下樹的直徑即可。看在環上的,如果最長路徑在環上,最長路徑一定有一條環上的邊沒有經過,假設 u v u,v 是沒有經過地邊,...

BZOJ3244 NOI2013樹的計數

給定一棵 n n 200000 個節點的樹的 df s bf s 序,求所有滿足要求的樹的平均深度。考慮到 bf s 序的性質,bf s 在前的點的深度一定小於等於後面的點。所以我們考慮根據 bf s 序計算答案。首先根據 bf s 序給樹上的點重編號,按 bf s 序的先後編成 1,2,n 考慮相...

BZOJ3244 NOI2013 樹的計數

我們知道一棵有根樹可以進行深度優先遍歷 dfs 以及廣度優先遍歷 bfs 來生成這棵樹的dfs序以及bfs序。兩棵不同的樹的dfs序有可能相同,並且它們的bfs序也有可能相同,例如下面兩棵樹的dfs序都是1 2 4 5 3,bfs序都是1 2 3 4 5 現給定乙個dfs序和bfs序,我們想要知道,...