Loading

【题解】「CEOI2021」 Newspapers

非常神的树上问题,和树上的数超现实树有的一拼。

样例非常小没有任何价值,考虑手算一下长度为 \(5\) 的链。

不难发现最优策略就是 \(2,3,4,4,3,2\),这给了我们很大的启发。

  • 1.一定不能有环,否则任意情况对方都有两条路可走,永远无法抓住对方。

  • 2.我们查询的过程类似于在树上移动,每次可以将路径上的点排除。一定是在相邻点上移动,否则如果对方在跳过的那个点上,你就永远不知道他的位置。

  • 3.我们需要扫两遍,因为如果将树黑白染色,扫一遍只能排除一种颜色。

所以对于链的情况,一定有解且解为 \(2\to n -1,n-1\to 2\),共 \(2n-2\) 次询问,可以得到 \(8\) 分。

扩展一下,如果链的某个节点上再接上一段会发生什么变化。

如果接上的是一个点,那么答案不会变化,因为该节点和连接的链上节点颜色相反,所以不是在第一遍被排除就是在第二遍被排除。

如果接上的链是两个点,那么我们只用多进行两次判断即可。

就是 \(1\to A\to3\to A\to 2\)\(2\to A\to 3\to A\to 1\)

如果接更多的节点呢,惊奇的发现我们不能更深入 \(4\) 号节点。

如果我们到达 \(4\) 号,再回到 \(A\),那么进行 \(3\to 4\to 3\) 三次操作。这个时候如果对手在 \(P\) 或者 \(Q\) 号节点上,三次操作足够对方跨过 \(A\) 到达另一边。那么自己回到 \(A\) 的时候根本不知道对手在哪边,相当于前面的努力都白费了。

所以如果出现这种情况,两边都无法保全,只可能是无解。

这么分析后最优解应该很清晰了,我们找到直径,然后判断是否有长度 \(\ge 3\) 的支链,然后对于长度为 \(2\) 的支链要多进行两次操作进行覆盖。

时间复杂度 \(\mathcal{O}(N)\),checker 的复杂度应该是 \(\mathcal{O}(N^2)\) 的。

代码很短。

#define N 1005
int n, m, d[N], fa[N], c[N], t, v[N]; vector<int>e[N], u[N], ans;
void dfs(int x,int f){
	d[x] = d[fa[x] = f] + 1;
	go(y, e[x])if(y != f)dfs(y, x);
}
void calc(int x,int f){
	go(y, e[x])if(y != f)calc(y, x), cmx(d[x], d[y]);
}
int main() {
	read(n, m);
	if(m >= n){puts("NO"); return 0;}
	if(n == 1){	puts("YES\n1\n1"); return 0;}
	if(n == 2){ puts("YES\n2\n1 1"); return 0;}
	rp(i, m){
		int x, y; read(x, y);
		e[x].pb(y), e[y].pb(x);
	}
	dfs(1, 0); int X = 1;
	rp(i, n)if(d[i] > d[X])X = i;
	dfs(X, 0); int Y = 1;
	rp(i, n)if(d[i] > d[Y])Y = i;
	int tem = Y;
	while(tem)v[c[++t] = tem] = 1, tem = fa[tem];
	rp(i, n)if(!v[i] && v[fa[i]]){
		calc(i, fa[i]);
		if(d[i] >= d[fa[i]] + 3){puts("NO"); return 0;}
		if(d[i] > d[fa[i]] + 1)u[fa[i]].pb(i);
	}
	puts("YES");
	rep(i, 2, t - 1){
		ans.pb(c[i]);
		go(x, u[c[i]])ans.pb(x), ans.pb(c[i]);
	}
	pre(i, t - 1, 2){
		ans.pb(c[i]);
		go(x, u[c[i]])ans.pb(x), ans.pb(c[i]);
	}
	printf("%d\n", si(ans));
	go(x, ans)printf("%d ", x); el;
	return 0;
}
posted @ 2022-03-15 17:01  7KByte  阅读(149)  评论(0编辑  收藏  举报