【题解】「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;
}