【题解】Codeforces Round #769 (Div. 2)

B

构造题经常会卡一下...
考虑最高位(设为d),一定存在两个相邻的数,最高位一个是1一个是0,异或起来答案就不会小于1<<d,然后发现可以构造使得答案等于1<<d

C

这么考虑:
假设不使用操作3,那么答案就是b-a
假设使用操作3,用完后a>=b,接下来一直使用操作2令b自加1直到结束。为什么?因为每次使用操作1或3都会使a变得更大,而b只能通过自加1变大,所以接下来的最优方案就是一直用操作2
那么使用操作3之前的策略是什么呢?注意到b的总和不超过1e6,考虑枚举,既然要枚举,那就设元。假设a自加到a',b自加到b',那么答案就是 \(a'-a+b'-b+1+((a'|b')-b')=a'+(a'|b')+(1-a-b)\),考虑 \(a'+(a'|b')\),其中 \(a\leq a' < b \leq b'\)
\(a'\) 有范围,枚举 \(a'\):此时令 \(b'\) 大等于 \(b\)\(a'|b'\) 尽量小
从高往低位考虑,,目标是在满足 \((a'|b')\) 尽量小的情况下,一旦满足 b'>b 就跑路
若当前位 1 = a' > b = 0,那 \(a'|b'\) 这一位肯定是 1 了,不妨直接当前位 b' 设为 1 满足 b'>b,之后的低位就全 0,然后break
否则的话,根据 a' 和 b 的情况讨论一下,然后继续考虑下一位

D

这题好像挺直球的(
注意到对于一个位置,一旦修改成一个足够大的未出现过的质数,那么它可以“切割”两边,即所有包含这个位置的区间的gcd都只会等于1
我们再设 f[i-1] = ans 表示前 i-1 个数的最小修改次数,以及上一个修改的位置是 p,那么对于当前的第 i 个数,我们只需要考虑区间 [p+1, i], [p+2, i], ..., [i, i]
注意到区间右端点不变,即r-l+1的r不变,而左端点往左扩时,gcd只会减小,r-l+1只会增大,两者最多一个交点,具有单调性,二分即可;若发现存在一个位置两者相等了,那么当前 i 修改,p 更新为 i,ans++
至于为什么不考虑当前位置 i 不修改而是去修改 i 之前的位置,因为 p 肯定越大越好,而且题面的条件是对任意子区间而言的

E1, E2

首先考虑加一条边带来的影响,是由这两个点dist变小,做为“源点”更新出去的
但是真的是 两 个点吗?如果是,那么需要满足 dist[i]> dist[j]+x 和 dist[j]> dist[i]+x,显然不能同时满足,所以最多只有其中一个点 u 的dist变小,既然如此,那么另一个点肯定得是根节点
easy版本的一个做法,只需注意到对于一个固定的 u,随着 x 的减小,其能影响到的点在增多;所以枚举每个点 u 作为连线点:不考虑加边下,设以 1 为根的树上点 i 深度 d,以 u 为根树上点 i 深度 dd,则其满足 d> dd+x 时会被更新,所以把所有点按 d-dd 排个序,然后从大往小枚举 x 即可
hard版本,需要推出一个性质:假设对于一个深度 d,所有深度大于 d 的节点中距离最远的两个点,若加边使得这两个点的dist都小等于 d,则其他所有点的dist都会小等于 d
这是个什么道理?我想了一下,大概是这个原因:对于树上的一些被标记点,取其距离最远的两个点 u, v,考虑这两个点之间的路径上的任意一点 mid,接下来考虑任意标记点 i 与 mid 的距离,画图只有两个情况:

情况 1,i 与 mid 的路径与 u, v 路径不相交,此时 i 与 mid 的距离必须不大于 mid 与 u, v 任意其一的距离,否则与距离最远矛盾
情况 2,i 与 mid 的路径与 u, v 路径相交,假设与 u, mid 一段相交,此时 i 与 mid 距离必须不大于 mid 与 u 的距离,否则矛盾
综上,回到题目,加边使得这两个点的dist小等于 d,显然需要把点加在两个点路径的中点处,假设两点距离 f,则需要满足:\(\lceil \frac{f}{2} \rceil+x\leq d\),此时其他点都满足dist小等于 d
对于一个固定的 d,我们求出对应的 \(f_d\),注意到随着d增大,\(f_d\) 是减小的;上式 x 单独移到一边,不等式另一边具有单调性,所以 d 作为答案具有单调性,二分答案或者双指针扫即可
\(f_d\) 怎么求?两个点之间的路径上肯定有其lca,通过lca求:对于每个点,求出其子树中最深和次深的两个 \(d_1, d_2\),则 \(f[d_2-1]=\max ( f[d_2-1], d_1+d_2-2d_{lca} )\),然后倒过来 \(f[d] = \max(f[d], f[d+1])\) 即可

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 300005;
int T;
int N, fst[MAXN];
int f[MAXN];
struct edge {
	int v, pre;
} e[MAXN<<1];
void adde(int a, int b, int k)
{
	e[k].v = b, e[k].pre = fst[a], fst[a] = k;
}
int init(int x, int fa, int d)
{
	int d1 = d, d2 = d;
	for (int o=fst[x]; o; o=e[o].pre) if (e[o].v!=fa) {
		int dd = init(e[o].v, x, d+1);
		if (dd> d1) d2 = d1, d1 = dd;
		else if (dd> d2) d2 = dd;
	}
	f[d2-1] = max(f[d2-1], d1-d+d2-d);
	return d1;
}
int main()
{
	for (scanf("%d", &T); T; T--) {
		scanf("%d", &N);
		for (int i=1; i<=N; i++) fst[i] = 0, f[i] = 0;
		for (int i=1; i< N; i++) {
			int u, v; scanf("%d%d", &u, &v);
			adde(u, v, i), adde(v, u, i+N);
		}
		int d = init(1, 0, 0);
		for (int i=N-2; i>=0; i--) f[i] = max(f[i], f[i+1]);
		int ans = 0;
		for (int x=1; x<=N; x++) {
			while (ans< d && (f[ans]+1)/2+x> ans) ans++;
			printf("%d ", ans);
		}
		puts("");
	}
}
posted @ 2022-02-03 23:45  zrkc  阅读(119)  评论(1编辑  收藏  举报