[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;更簡單的做法就是讓 bfs 序變成 1~n 的排列,dfs 序隨之變化,然後分情況討論節點 i 和 i-1 必定不在同一層,可以在同一層也可以不在同一層,必定在同一層,貢獻分別是 1、0.5 和 0。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;
}
以下省略條件:對於節點 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序,我們想要知道,...