【BZOJ3244】【UOJ122】【NOI2013】树的计数

【题目链接】

【思路要点】

  • 显然,顶点的编号是无关紧要的,不妨令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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值