2021.7.14 校内模拟赛游记

前面两题基础 dp 写得有点慢了,明明是没有任何高级技巧的纯粹基础 dp 居然写了两个小时。

A

\(n\) 个点的 \(m\) 叉树的个数。

因为初始状态的转移的小问题调了很久,FDR 记录显示到 \(53\) 分钟才调出来,然而代码才五行。。。

首先想到的状态是 \(f_{i, j}\) 表示 \(i\) 个点 \(j\) 叉树有多少的方案,可是这样似乎没有办法转移,因为一个叉数的数好像和其它叉数的没有什么关系。于是增加一维, \(f_{i, j, k}\) 表示 \(i\) 个点 \(j\) 叉且根连了 \(k\) 叉的方案数,这样只要枚举根加进去的最后一叉有多少个就可以转移了。

容易发现,这个时候 \(j\) 已经没有什么用了,因为当前叉可以接 \(0\), 所以这个 \(k\) 完全包含了 \(j\) 的作用。

最后要注意初始状态 \(f_{0, i} = f_{1, i} = 1\),毕竟只有一个点和没有点总是只有一种方法。因为这个问题调了很久。

B

每次可以给连续一段涂上一种颜色,求把 \(A\) 变成 \(B\) 的方案数。

先思考如果从空白开始涂怎么处理,按照区间 dp 的套路开始考虑合并的时候答案的变化。

如果最左最右相同那么显然是可以在最开始的时候把这俩刷了,所以答案会减去 \(1\),但是随便写几个串似乎可以找到“反例”, 比如 \(\textsf{taab}\)\(\textsf{babt}\),两边都是 \(3\) 但合起来是 \(4\)。难道还要考虑减少多个的状态吗?

其实不必,别忘了我们还要枚举中间的断点 \(k\),如果有两个可以和左/右相连的,那么我们在那里断开,一边就是左右被连上的,所以这个减少会被记进答案之中,同理,所有的减少都会记进答案之中。在上面的例子中,如果把它分成 \(\textsf{taabbab}\)\(\textsf{t}\) 就可以得到正确答案了。

这个思想非常的重要。

然后考虑原问题,合并的时候也让它在左右端点一样就减去 \(1\),按照上面一样的思路,这样也可以得到正确的答案,那么只要把初值赋为上面 dp 的结果然后再做一遍 dp 就可以了。

幸好这场比赛中严格执行了检查单,不然这又要挂分了。

C

LYC_music 认为这是全场最简单的一题,一眼秒杀,但我却想了一个多小时未果。

多重集支持单点修改,求 \(\sum x \bmod a_i\)

看到数据范围中每一个的个数和每一个数的范围都只有 \(10^5\),那么显然可以用计数来表示多重集。很容易想到搞个啥值域数据结构来处理。

然后……就没有然后了,一直想不到正解。

其实可以把 \(\bmod\) 拆开成 \(x - i\lfloor \frac{x}{a_i} \rfloor\),然后把 \(\sum\) 也拆开成 \(\sum x - \sum i\lfloor \frac{x}{a_i} \rfloor\),前面那个东西直接记,后面的部分因为有很多的 \(\lfloor \frac{x}{a_i} \rfloor\) 都一样,数论分块后用树状数组处理就可以了。

D

出题人眼中的最简单???

给定一棵完全二叉树,支持删边后查询一个点和其所在块的中心之间的距离。

很套路地把删变成加,然后根据重心的性质:

两树合并后重心在原重心的路径上。

在路上跳一遍看新重心是哪个就可以了。
因为保证了是完全二叉树,所以树高 \(\log\) 级别,跳——包括 LCA——都可以直接跳。

然后就……没了。

代码实现感觉还是有一点技巧的。
因为众所周知的原因,这题代码写得有点奇怪。

展开看代码
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
const int N = 50005;
int n, x[N], y[N], p[N], q[N], rt[N], ms[N], size[N], ans[N], 
	f[N], dep[N], m, fa[N], siz[N];
bool flag[N];
std::map<int, bool> ff[N];
std::vector<int> g[N];
int find(int x) { return (x == fa[x]) ? x : (fa[x] = find(fa[x])); }
void dfs(int u, int par) {
	dep[u] = dep[par] + 1, f[u] = par;
	for (int i = 0; i < (signed)g[u].size(); i++) {
		int v = g[u][i];
		if (v == par) continue;
		dfs(v, u);
	}
}
int lca(int x, int y) {
	if (dep[x] < dep[y]) std::swap(x, y);
	while (dep[x] > dep[y]) x = f[x];
	if (x == y) return x;
	while (f[x] != f[y]) x = f[x], y = f[y];
	return f[x];
}
void get(int u, int id) {
	ms[u] = 0, siz[u] = 1;
	for (int i = 0; i < (signed)g[u].size(); i++) {
		int v = g[u][i];
		if (v == f[u] || ff[u][v]) continue;
		siz[u] += siz[v];
		ms[u] = std::max(ms[u], siz[v]);
	}
	ms[u] = std::max(ms[u], size[id] - siz[u]);
	if (ms[u] < ms[rt[id]] ||
	   (ms[u] == ms[rt[id]] && u < rt[id])) rt[id] = u;
}
void merge(int u, int v) {
	fa[v] = u, size[u] += size[v];
	int id = u;
	u = rt[u], v = rt[v];
	rt[id] = 0;
	while (u != v) {
		if (dep[u] > dep[v]) get(u, id), u = f[u];
		else get(v, id), v = f[v];
	}
	get(u, id);
}
int main() {
	freopen("dislct.in", "r", stdin);
	freopen("dislct.out", "w", stdout);
	
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &x[i], &y[i]);
		g[x[i]].push_back(y[i]), g[y[i]].push_back(x[i]);
	}
	scanf("%d", &m);
	dfs(1, 0);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &p[i], &q[i]);
		if (p[i] == 1) 
			flag[q[i]] = 1, 
			ff[x[q[i]]][y[q[i]]] = ff[y[q[i]]][x[q[i]]] = 1;
	}	
	ms[0] = 1000000000;
	for (int i = 1; i <= n; i++) fa[i] = i, size[i] = 1, rt[i] = i;
	for (int i = 1; i < n; i++) 
		if (!flag[i]) {
			int fx = find(x[i]), fy = find(y[i]);
			if (fx == fy) continue;
			merge(fx, fy);
//			printf("*%d\n", ms[1]);
		}
	for (int i = m; i >= 1; i--) {
		if (p[i] == 1) ff[x[q[i]]][y[q[i]]] = ff[y[q[i]]][x[q[i]]] = 0, merge(find(y[q[i]]), find(x[q[i]]));
		else if (p[i] == 2) {
			int u = rt[find(q[i])], v = q[i];
			int l = lca(u, v);
			ans[i] = dep[u] + dep[v] - 2*dep[l];
		}
		else "I won't solve the problem when the operation code is THREE", 
			 "because of the man who wrote this problem's Gugugu!";
	}
	for (int i = 1; i <= m; i++)
		if (p[i] == 2) printf("%d\n", ans[i]);
	return 0;
}

总结

这场模拟赛前三题还是非常可做的,可是还是没有想出 C。

前两题严格执行了标准操作程序和检查单,获得了满分,但在做 C 的时候没有执行跳过程序,以至于没有去想 D 或者写 D 的暴力,仍需改进。

posted @ 2021-07-15 08:39  Acfboy  阅读(64)  评论(0)    收藏  举报