【题目链接】
【思路要点】
- 显然,顶点的编号是无关紧要的,不妨令BFS序为\(1,2...N\)。
- 我们在BFS序上分层,即每次我们考虑下一层会包含哪些连续的点。
- 令此时的DFS序为\(dfs_i\),\(i\)在DFS序中的位置为\(pos_i\),当前层包含\([L,R]\)且下一层包含\([R+1,X]\)。
- 层与层之间合法的划分应该满足\(pos_{R+1}>pos_{L}\)且\(Min_{i=R+1}^{Max_{j=R+1}^{X}\{pos_j\}}\{i\}>=L\)。
- 直接按照这个性质DP,可以得到一个复杂度上界为\(O(N^3)\)的做法,但实际上这个做法可以快速地通过\(N≤2000\)的数据,因此笔者猜想这个DP拥有着更加优秀的复杂度(尽管笔者不会证明这一点)。
- 在考场上,这个做法可以得到85分,这也是笔者认为可行的考场做法,想要在考场上完成正解对于大多数人来说应该是不切实际的。
- 进一步思考,我们发现所求的树之所以会有很多种,是因为出现了这种情况:对于A、B,A既可以做B的兄弟,又可以做B的父亲(如样例)。
- 考虑\(i\)和\(i+1\),如果\(pos_i>pos_{i+1}\),那么\(height_i+1=height_{i+1}\),因为同一层的点在DFS和BFS序的相对位置应该是一致的。这种情况对答案的贡献为1。
- 否则,如果\(pos_i+1\ne pos_{i+1}\),那么\(height_i=height_{i+1}\),因为在此情况下\(i+1\)不可能是\(i\)的第一个子节点,也不可能是\(i\)的其他子节点,所以只有可能是\(i\)的兄弟。这种情况对答案的贡献为0。
- 否则,即\(pos_i+1=pos_{i+1}\),也就是前面所说的A既可以做B的兄弟,又可以做B的父亲的情况。
- 但不是所有这样的情况\(i\)都既可以做\(i+1\)的兄弟,又可以做\(i+1\)的父亲,我们需要注意到的是\(height_{dfs_i}+1≥height_{dfs_{i+1}}\),因此,有一些出现这种情况的地方\(i\)只可以做\(i+1\)的兄弟,而不能做父亲。
- 因此,我们用差分+前缀和的思想找到所有一定只能做兄弟的情况;剩下的就是\(i\)既可以做\(i+1\)的兄弟,又可以做\(i+1\)的父亲的情况了,显然,这种情况对答案的贡献为0.5。
- 时间复杂度\(O(N)\)。
- 在BZOJ上提交时,需要遵从一定的奇怪格式,应当是数据添加不当导致的,详见代码。
【代码】
/*BruteForce which got 85*/ #include<bits/stdc++.h> using namespace std; const int MAXN = 3005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } long double sum[MAXN][MAXN], cnt[MAXN][MAXN]; int dfs[MAXN], bfs[MAXN], num[MAXN], dep[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(dfs[i]), num[dfs[i]] = i; for (int i = 1; i <= n; i++) read(bfs[i]), dep[bfs[i]] = i; sum[1][1] = cnt[1][1] = 1; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) { if (cnt[i][j] == 0) continue; int pos = num[bfs[j]], Min = n; for (int k = j + 1; k <= n; k++) { if (num[bfs[k]] < num[bfs[i]]) break; if (k != j + 1 && num[bfs[k]] < num[bfs[k - 1]]) break; while (pos <= num[bfs[k]]) Min = min(Min, dep[dfs[pos++]]); if (Min < i) break; cnt[j + 1][k] += cnt[i][j]; sum[j + 1][k] += cnt[i][j] + sum[i][j]; } } long double s = 0, c = 0; for (int i = 1; i <= n; i++) s += sum[i][n], c += cnt[i][n]; printf("%.3Lf\n", s / c); return 0; } /*UOJ Version*/ #include<bits/stdc++.h> using namespace std; const int MAXN = 200005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int fix[MAXN], val[MAXN], sum[MAXN]; int dfs[MAXN], bfs[MAXN], pos[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(dfs[i]); for (int i = 1; i <= n; i++) read(bfs[i]), pos[bfs[i]] = i; for (int i = 1; i <= n; i++) dfs[i] = pos[dfs[i]], bfs[i] = pos[bfs[i]]; for (int i = 1; i <= n; i++) pos[dfs[i]] = i; val[1] = 1, fix[1] = 1, fix[2] = -1; for (int i = 1; i <= n - 1; i++) if (pos[i] > pos[i + 1]) { val[i] = 1; fix[i]++; fix[i + 1]--; } for (int i = 1; i <= n; i++) sum[i] = val[i] + sum[i - 1]; for (int i = 1; i <= n - 1; i++) if (dfs[i] < dfs[i + 1] && (sum[dfs[i + 1] - 1] - sum[dfs[i] - 1]) != 0) { fix[dfs[i]]++; fix[dfs[i + 1]]--; } double ans = 1 + sum[n]; for (int i = 1; i <= n - 1; i++) { fix[i] += fix[i - 1]; if (fix[i] == 0) ans += 0.5; } printf("%.3lf\n", ans); return 0; } /*BZOJ Version*/ #include<bits/stdc++.h> using namespace std; const int MAXN = 200005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int fix[MAXN], val[MAXN], sum[MAXN]; int dfs[MAXN], bfs[MAXN], pos[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(dfs[i]); for (int i = 1; i <= n; i++) read(bfs[i]), pos[bfs[i]] = i; for (int i = 1; i <= n; i++) dfs[i] = pos[dfs[i]], bfs[i] = pos[bfs[i]]; for (int i = 1; i <= n; i++) pos[dfs[i]] = i; val[1] = 1, fix[1] = 1, fix[2] = -1; for (int i = 1; i <= n - 1; i++) if (pos[i] > pos[i + 1]) { val[i] = 1; fix[i]++; fix[i + 1]--; } for (int i = 1; i <= n; i++) sum[i] = val[i] + sum[i - 1]; for (int i = 1; i <= n - 1; i++) if (dfs[i] < dfs[i + 1] && (sum[dfs[i + 1] - 1] - sum[dfs[i] - 1]) != 0) { fix[dfs[i]]++; fix[dfs[i + 1]]--; } double ans = 1 + sum[n]; for (int i = 1; i <= n - 1; i++) { fix[i] += fix[i - 1]; if (fix[i] == 0) ans += 0.5; } printf("%.3lf\n%.3lf\n%.3lf\n", ans - 0.001, ans, ans + 0.001); return 0; }