[题解]AtCoder Beginner Contest 397(ABC397) D~F

D - Cubes

此处为题面跳转

解题思路

常规错解:枚举 \(x\) 通过 \(N\) 来找 \(y\) 是否满足条件,会导致 TEL。

正解:通过换元公式计算来将 \(x\)\(y\) 独立出来判断。

\[a = x + y, \quad b = x - y \]

\[x = \frac{a + b}{2}, \quad y = \frac{a - b}{2} \]

\[x^3 - y^3 = (x - y)(x^2 + xy + y^2) = b \left( x^2 + xy + y^2 \right) \]

\[x^3 - y^3 = (x - y)(x^2 + xy + y^2) = b \left( x^2 + xy + y^2 \right) \]

\[x^2 + xy + y^2 = \left( \frac{a + b}{2} \right)^2 + \left( \frac{a + b}{2} \right)\left( \frac{a - b}{2} \right) + \left( \frac{a - b}{2} \right)^2 \]

\[\frac{a^2 + 2ab + b^2}{4} + \frac{a^2 - b^2}{4} + \frac{a^2 - 2ab + b^2}{4} = \frac{3a^2 + b^2}{4} \]

\[b(3a^2 + b^2) = 4N \]

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;

// a = x + y, b = x - y;
// b * (3 * a * a + b * b) = n
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n; cin >> n;
	n *= 4;
	
	for (int b = 1; b * b * b <= n; b++) {
		if (n % b) continue;
		int res = n / b;
		res -= b * b;
		if (res % 3) continue;
		res /= 3;
		int a = sqrt(res);
		if (a * a != res) continue;
		int x = (a + b) / 2;
		int y = (a - b) / 2;
		if (x > 0 && y > 0) {
			cout << x << " " << y << "\n";
			return 0;
		}
	}
	
	cout << -1 << "\n";
	
	return 0;
}




E - Path Decomposition of a Tree

此处为题面跳转

解题思路

得到一颗 \(n\) * \(k\) - \(1\) 大小的树,存在边的过程等于将子树 \(u\) 的节点全部用链覆盖,即用树形dp从叶子节点往上推即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;

// a = x + y, b = x - y;
// b * (3 * a * a + b * b) = n
const int N = 2e5 + 7;
int cnt[N];
vector<int> e[N];
int f = 0;
int n, k;

void dfs(int u, int fa) {
	if (f == 1) return;
	int sum = 0;
	for (auto v: e[u]) {
		if (v != fa) {
			dfs(v, u);
			cnt[u] += cnt[v];
			sum += cnt[v] > 0;
		}
	}
	cnt[u]++;
	if (cnt[u] > k || (cnt[u] == k && sum >= 3) || (cnt[u] < k && sum >= 2)) {
		f = 1;
		return;
	}
	else if (cnt[u] == k) cnt[u] = 0;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> n >> k;
	
	for (int i = 1; i < n * k; i++) {
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	
	dfs(1, 0);
	if (!f) cout << "Yes" << "\n";
	else cout << "No" << "\n";
	return 0;
}




F - Variety Split Hard

此处为题面跳转

解题思路

此题为 \(C\) 题的 \(hard\) 版本,故可以从 \(C\) 题中得到启发,首先还是预处理出来 \(pre\)\(suf\) 数组来表示 \(i\) - \(j\) 每个点的前缀最大种类数和后缀最大种类数。

所有我们就可以先从后往前枚举一个断点 \(i\) ,记 \(num1\)\(pre[i]\)\(num2\)\(suf[i - 1]\)

接下来我们需要在后半个区间再增加一个断点让整个区间划为三段符合题意,如何找到使得种类增加最多的位置即为本题的本质所在。

我们可以发现在一对相等的数之间增加断点即可使整个区间的贡献加一,且可以简单推到出后半段的总和 \(sum = suf[i - 1] + num3\) ,这里的 \(num3\) 就是区间最大的贡献,因此我们可以用 \(map\) 来记录每个数
字出现的最近位置,然后让这整个区间的贡献加一即可,每次枚举过程查询一下 \(query(1, n, i, n, 1)\) 就可以得到当前的最大贡献。

实现区间的最大值查询、区间的加法,显而易见是线段树,按着线段树模板稍微改改即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N = 3e5 + 7;
int a[N], pre[N], suf[N];

int tr[N * 4], tg[N * 4];

void build (int l, int r, int p) {
	if (l == r) {
		tr[l] = 0;
		return;
	}
	int mid = l + r >> 1;
	build (l, mid, p * 2);
	build (mid + 1, r, p * 2 + 1);
	tr[p] = max(tr[p * 2], tr[p * 2 + 1]);
}

void push_down (int l, int r, int m, int p) {
	if (!tg[p]) return;
	tr[p * 2] += tg[p] ;
	tr[p * 2 + 1] += tg[p];
	tg[p * 2] += tg[p];
	tg[p * 2 + 1] += tg[p];
	tg[p] = 0;
}

void update (int ql, int qr, int l, int r, int p) {
	if (ql <= l && qr >= r) {
		tr[p] ++;
		tg[p] ++;
		return;
	}
	int mid = l + r >> 1;
	push_down(l, r, mid, p);
	if (ql <= mid) update(ql, qr, l, mid, p * 2);
	if (qr > mid) update(ql, qr, mid + 1, r, p * 2 + 1);
	tr[p] = max(tr[p * 2], tr[p * 2 + 1]);
}

int query (int ql, int qr, int l, int r, int p) {
	if (ql <= l && qr >= r) return tr[p];
	int mid = l + r >> 1, mx = 0;
	push_down(l, r, mid, p);
	if (ql <= mid) mx = max(mx, query(ql, qr, l, mid, p * 2));
	if (qr > mid) mx = max(mx, query(ql, qr, mid + 1, r, p * 2 + 1));
	return mx; 
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n; cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i]; 
	
	int ans = 0;
	
	map<int, int> mp;
	for (int i = 1; i <= n; i++) {
		pre[i] = pre[i - 1];
		if (!mp[a[i]]) {
			mp[a[i]] = 1;
			pre[i]++;
		}
	}
	mp.clear();
	for (int i = n; i >= 1; i--) {
		suf[i] = suf[i + 1];
		if (!mp[a[i]]) {
			mp[a[i]] = 1;
			suf[i]++;
		}
	}
	mp.clear();
	
	build(1, n, 1);
	for (int i = n; i >= 1; i--) {
		int num1 = pre[i - 1], num2 = suf[i];
		if (mp[a[i]] != 0) {
			update(i, mp[a[i]] - 1, 1, n, 1);	
		}
		int num3 = query(1, n, i, n, 1);
		//cout << num1 << " " << num2 << " " << num3 << "\n";
		mp[a[i]] = i;
		ans = max(ans, num1 + num2 + num3);
	}
	cout << ans << "\n";
	return 0;
}
posted @ 2025-04-03 03:07  type-umr  阅读(55)  评论(0)    收藏  举报