AtCoder Beginner Contest 397

ABC397D Cubes

我们有 \(N = (x-y)(x^2+xy+y^2) = ab\),由于 \((x-y)^2 \le (x^2+xy+y^2)\),那么 \(a\)\(O(N^{1/3})\) 级别的数,可以直接枚举,check 就是个解二次方程。

void slv() {
	i128 n; Read(n);
	
	constexpr i128 lim = 5e6;
	auto chk = [&](i128 x) -> i128 {
		i128 l = 0, r = 1e18;
		while (l < r) {
			i128 mid = (l + r + 1) / 2;
			if (mid * mid <= x) {
				l = mid;
			} else {
				r = mid - 1;
			}
		}
		return l;
	};
	for (i128 a = 1; a < lim; a ++) {
		if (n % a) continue;
		i128 b = n / a, xy = b - a * a;
		if (xy % 3) continue;
		xy /= 3;
		i128 c = a * a + 2 * b;
		if (c % 3) continue;
		c /= 3, c += 2 * xy;
		i128 d = chk(c);
		if (d * d != c) continue;
		i128 x = a + d, y = d - a;
		if (x & 1) continue;
		if (y & 1) continue;
		x /= 2, y /= 2;
		if (x <= 0 || y <= 0) continue;
		Write(x, ' ', y, '\n');
		return;
	}
	
	Puts("-1");
	
	return;
}

ABC397E Path Decomposition of a Tree

子树 \(u\) 内划分完之后一定是剩一条根链,且长度恰好为 \(len_u = size_u \bmod k\)

进行 DP,记 \(f_u\) 表示 \(u\) 子树内是否能成功划分。

首先要求所有儿子的子树内都能成功划分,记 \(\displaystyle cnt = \sum_{v \in \mathbf{son}(u)} [len_v \neq 0]\)

\(len_u = 1\) 时,要求 \(cnt = 0\),否则要求 \(cnt \neq 0\)

\(len_u \neq 0\) 时,要求 \(cnt = 1\),此时恰好形成一个根链。

\(len_u = 0\) 时,如果 \(cnt = 1\),那么是拼出一个根链,否则是两个子树中的根链和 \(u\) 拼成一条链。

constexpr int N = 2e5 + 5;
int n, k, sz[N], len[N];
vector<int> G[N];
bool f[N];

void DFS(int u, int fa) {
	sz[u] = 1, f[u] = true;
	int cnt = 0;
	for (auto v : G[u]) if (v != fa) {
		DFS(v, u), sz[u] += sz[v], f[u] &= f[v];
		cnt += !!len[v];
	}
	len[u] = sz[u] % k;
	if (!f[u]) {
		return;
	}
	if (len[u] == 1) {
		f[u] = !cnt;
		return;
	}
	if (len[u] != 0) {
		f[u] = (cnt == 1);
		return;
	}
	if (len[u] == 0) {
		f[u] = (1 <= cnt && cnt <= 2);
	}
	return;
}

void slv() {
	Read(n, k);
	
	if (k == 1) {
		Yes();
		return;
	}
	
	const int nk = n * k;
	
	for (int i = 1; i < nk; i ++) {
		int u, v; Read(u, v);
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	
	DFS(1, 0);
	
	Yes(f[1]);
	
	return;
}

ABC397F Variety Split Hard

按照套路,求出每个位置的颜色上一次出现的位置记为 \(lst_i\),并把每个位置看成点 \((i, lst_i)\),那么题目中要的就是将平面划分成 \(3\) 个对角线为 \(y = x\) 的正方形,使得正方形外的点最多。

考虑扫描 \(r\),同时维护 \(f_i\) 表示 \(l = i\) 时的前 \(r\) 个位置的贡献,\((r + 1, n]\) 这部分的答案 \(g_r\) 可以通过 C 题相同的方式维护,那么我们每次要做的就是 \(\displaystyle ans \leftarrow f_i + g_r\)

每次 \(r\) 移动时,首先将 \(f_r\) 加到 \(f\) 中,这个可以用和 C 题相同的方式维护,然后需要用 \(r\) 位置更新 \(f\),发现只会使 \([lst_i +1, i]\) 这段的 \(f\) 增加 1,最后计算答案。

发现需要做的是 push_back,后缀加,查询全局最大,可以并查集做到线性,不过没必要。

struct Info {
	int mx;
	
	Info(): mx(0) { return; }
	Info(int x): mx(x) {}
	friend Info operator + (Info x, Info y) {
		return Info(max(x.mx, y.mx));
	}
};
struct Tag {
	int tg;
	
	Tag(): tg(0) { return; }
	Tag(int t): tg(t) { return; }
	operator bool() {
		return tg;
	}
	friend Tag operator * (Tag x, Tag y) {
		return Tag(x.tg + y.tg);
	}
	Info Apply(Info x) {
		return x.mx += tg, x;
	}
};

constexpr int N = 3e5 + 5;
int n, a[N], bin[N], lst[N];
Segment_Tree<Info, Tag, N> sgt;

struct Counter {
	int cnt[N], res;
	
	Counter(): res(0) {
		memset(cnt, 0, sizeof cnt);
	}
	void add(int x) {
		res += !cnt[x] ++;
		return;
	}
	void del(int x) {
		res -= !-- cnt[x];
		return;
	}
	int qry() {
		return res;
	}
};

void slv() {
	Read(n);
	
	for (int i = 1; i <= n; i ++) {
		Read(a[i]);
	}
	sgt.Build(n + 1);
	
	Counter suf, pre;
	for (int i = 1; i <= n; i ++) {
		lst[i] = bin[a[i]], bin[a[i]] = i;
		suf.add(a[i]);
	}
	
	int ans = 0;
	for (int i = 1; i < n; i ++) {
		suf.del(a[i]), pre.add(a[i]);
		if (max(lst[i], 1) < i) {
			sgt.Update(max(lst[i], 1), i - 1, 1);
		}
		if (i > 1) {
			cmax(ans, sgt.Query().mx + suf.qry());
		}
		sgt.Change(i, pre.qry());
	}
	
	Write(ans, '\n');
	
	return;
}

ABC397G Maximize Distance

没写完,唉唉。

考虑去计算 \(F(x)\) 表示使得 \(1 \rightsquigarrow n\) 的最短距离为 \(x\) 至少改多少条边。

将问题看成给每个点标号,经过转化就是最小割的形式了。

然后二分即可。

constexpr int inf = 1E5;

void slv() {
	int n, m, k;
	Read(n, m, k);
	
	vector<pair<int, int>> edge(m);
	for (int i = 0; i < m; i ++) {
		int u, v; Read(u, v);
		edge[i] = {-- u, -- v};
	}
	
	auto calc = [&](int x) {
		MaxFlow_Graph<int> G(n * x + 2);
		int S = n * x, T = S + 1;
		
		auto id = [&](int i, int d) {
			return i * x + d;
		};
		
		for (int i = 0; i < n; i ++) {
			G.Add_Edge(id(i, 0), S, inf);
			G.Add_Edge(T, id(i, x - 1), inf);
			for (int j = 0; j < x - 1; j ++) {
				G.Add_Edge(id(i, j + 1), id(i, j), inf);
			}
		}
		G.Add_Edge(id(0, 0), T, inf);
		G.Add_Edge(S, id(n - 1, x - 1), inf);
		for (auto [u, v] : edge) {
			for (int j = 0; j < x; j ++) {
				if (j + 1 < x) {
					G.Add_Edge(id(v, j + 1), id(u, j), inf);
				}
				G.Add_Edge(id(v, j), id(u, j), 1);
			}
		}
		
		return G.Max_Flow(S, T);
	};
	
	int l = 0, r = n - 1;
	while (l < r) {
		int mid = (l + r + 1) / 2;
		if (calc(mid) <= k) {
			l = mid;
		} else {
			r = mid - 1;
		}
	}
	
	Write(l, '\n');
	
	return;
}
posted @ 2025-03-17 12:27  definieren  阅读(183)  评论(0)    收藏  举报