Codeforces Round 858 (Div. 2)

A CF1086A Walking Master

解一个二元一次方程组就行,注意特殊判断无解的情况。

B CF1086B Mex Master

Description

给定一个含 \(n\) 个非负整数的序列 \(a\),求对 \(a\) 重新排序后序列的最小分数。

一个序列 \(b\) 的分数为:不包含在序列 \([b_1+b_2,b_2+b_3,...,b_{n-1}+b_n]\) 中的最小非负整数。

Solution

当序列 \(a\)\(0\) 的个数不大于 \(\lceil n/2 \rceil\) 时,可以将序列排成 \([0,x,0,x,...](x\ne 0)\) 的样子,\(0\)不在该序列中,答案为\(0\)

否则,若序列全为 \(0\) ,则答案为 \(1\)

否则,或序列中的最大值比 \(1\) 大时,设它为 \(x\),可以将序列排成 \([0,...,0,x,a_i,...,a_k]\),由于\(x>1,a_i\ne0\),故答案为 \(1\)

否则,即序列中仅有 \(1\)\(0\),且 \(0\) 的个数比 \(1\) 多,可以将序列排成 \([0,...,0,1,0,1,0,1]\),则答案为\(2\)

Code

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5 + 5;
int n, a[N], ans = -1;
int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> n;
		int ind = 0, x, cnt_0 = 0, maxa = 0;
		for (int i = 1;i <= n;++i) {
			cin >> x;
			if (x == 0) {
				++cnt_0;
				continue;
			}
			a[++ind] = x;
			maxa = max(maxa, x);
		}
		if (cnt_0 <= (n + 1) / 2) {
			cout << "0" << endl;
			continue;
		}
		if (maxa > 1 || maxa == 0) {
			cout << "1" << endl;
			continue;
		}
		cout << "2" << endl;
	}
	return 0;
}

CF1086C Sequence Master

Description

对于正整数 \(n\) ,构造有 \(2n\) 个元素的序列,满足:从中任意取出 \(n\) 个元素,它们的乘积与剩下 \(n\) 个元素的相等。

给定长度为 \(2n\) 的序列 \(p\),构造满足上述条件的序列 \(a\),使得 \(\sum_{i=1}^{2n} |a_i-p_i|\) 最小。

只需输出最小的 \(\sum_{i=1}^{2n} |a_i-p_i|\),无需输出构造的序列 \(a\)

Solution

首先考虑如何构造序列 \(a\)

注意到,满足条件的 \(a\) 是很少数的。

① 容易发现,\(a_1=a_2=...=a_n=0\) 是一个解,则答案为 \(\sum |p_i|\)

\(n=1\)时,只需满足 \(a_1=a_2\)即可,易得答案为 \(|p_1-p_2|\)

③ 若所有元素相等且不为 \(0\),即\(a_1=a_2=...=a_n\ne0\)

​ 即需解出 \(na_1=a_1^n \rightarrow n=a_1^{n-1}\) ,仅有一个解:\(n=2,a_1=a_2=a_3=a_4=2\)

④ 若有不等的元素,假设 \(a_1\ne a_2\) ,有:

\(a_1a_3...a_na_{n+1}=a_2+a_{n+2}+...+a_{2n}\) (1)

\(a_2a_3...a_na_{n+1}=a_1+a_{n+2}+...+a_{2n}\) (2)

\(a_1a_2...a_{n-1}a_{n}=a_{n+1}+a_{n+2}+...+a_{2n}\) (3)

(1)-(2)得:\((a_1-a_2)a_3...a_{n+2}=a_2-a_1\)

因为 \(a_1\ne a_2\),所以 \(a_3a_4....a_{n+2}=-1\)

需要满足任从\(\{a_3,a_4,...,a_{2n}\}\)中取 \(n-1\) 个数,它们得乘积都为 \(-1\)

\(a_3=a_4=...=a_{2n}\)

注意到只有 \(n\) 为偶数时,这种情况才有解 \(a_3=a_4=...=a_{2n}=-1\) (4)

将(4)代入(1)得:\(a_1+a_2=n-1\) (5)

将(4)代入(3)得:\(a_1a_2=-n\) (6)

联合(5)和(6)解得:\(a_1=n,a_2=-1\)\(a_1=-1,a_2=n\)

综上所述,当$n%2=0 \(时,\){a}$有解 \(\{n,-1,-1,...,-1\}\)

那么接下来只需要确定把 \(n\) 放在序列中的哪个位置即可:放在 \(|p_i+1|-|p_i-n|\)最大的位置。

Conclusion

写下容易得到的特殊解

\(n\) 从小到大进行讨论;

元素都相等和有不等两种情况讨论:

相等的情况直接解方程,

不等的情况,假设出两个不等的元素,写下等式并进行运算(如相减),解出答案。

注意特殊情况前提条件

Code

#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
const int N = 4e5 + 5;
int n, p[N];
ll ans;
ll min(ll x, ll y) {
	return x < y ? x : y;
}
int main() {
	int T;
	cin >> T;
	while (T--) {
		ans = 0;
		cin >> n;
		for (int i = 1;i <= n * 2;++i) {
			cin >> p[i];
			ans += abs(p[i]);
		}
		if (n == 1) //特殊情况勿漏
			ans = min(ans, abs(p[1] - p[2]));
		if (n == 2) {
			ll tmp_ans = 0;
			for (int i = 1;i <= n * 2;++i)
				tmp_ans += abs(p[i] - 2);
			ans = min(ans, tmp_ans);
		}
		if (n % 2 == 0) { //清楚前提条件
			int pos = 1, val = abs(p[1] + 1) - abs(p[1] - n);
			for (int i = 1;i <= n * 2;++i)
				if (val < abs(p[i] + 1) - abs(p[i] - n))
					pos = i, val = abs(p[i] + 1) - abs(p[i] - n);
			ll tmp_ans = 0;
			for (int i = 1;i <= n * 2;++i)
				if (i == pos) tmp_ans += abs(p[i] - n);
				else tmp_ans += abs(p[i] + 1);
			ans = min(ans, tmp_ans);
		}
		cout << ans << endl;
	}
	return 0;
}

CF1086 D DSU Master

CF1086E Tree Master

Description

给定一棵有 \(n\) 个结点的树,结点标号从 \(1\)\(n\),每个结点 \(i\) 有一个权值 \(i\) 。记结点 \(i\) 的父结点为 \(p_i\) ,树根 \(1\) 的父结点记为 \(0\)

对于结点 \(x\)\(y\),定义 \(f(x,y)\) 为:

​ 初始 \(ans=0\)

\(while(x\)\(y\) 都不为 \(0\) \()\)

​ · \(ans\leftarrow ans+a_x*a_y\)

​ · \(x\leftarrow p_x\)\(y\leftarrow p_y\)

\(f(x,y)=ans\)

给出 \(q\) 个询问,对于第 \(i\) 个询问给出的 \(x_i,y_i\),求出 \(f(x_i,y_i)\)

Solution

记忆化搜索。

注意\(map\) 来记忆化会TLE,需要记录在数组中,使得用\(O(1)\)的时间可以完成一次查询。具体来说,记录 \(rem[x][i]\) 为第 \(x\) 个结点与同层编号(注意这个编号不是结点标号,而是对树的某一层从\(1\)开始的编号)为 \(i\) 的结点的答案。由于空间的限制,我们只能记录某一层的总结点数小于某个值(我设的是\(1000\))时的答案。

可以证明记搜的时间和空间复杂度都是\(O(n\sqrt n)\):【没看懂这个证明,还在研究】

Code

#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#include<cstring>
#define ll long long
using namespace std;
const int N = 1e5 + 5, M = 1000;
int n, q, a[N], p[N], cnt[N], dep[N], num[N];
ll rem[N][M];
vector<int>to[N];
ll calc(int x, int y) {
	if (x == 0)
		return 0;
	int d = dep[x];
	if (cnt[d] < M && rem[x][num[y]] != -1)
		return rem[x][num[y]];
	ll ret = 1ll * a[x] * a[y] + calc(p[x], p[y]); //注意这里的1ll
	if (cnt[d] < M)
		rem[x][num[y]] = ret;
	return ret;
}
void dfs(int u, int depth) {
	dep[u] = depth;
	num[u] = ++cnt[dep[u]];
	for (int v : to[u])
		dfs(v, depth + 1);
}
int main() {
	cin >> n >> q;
	for (int i = 1;i <= n;++i) cin >> a[i];
	for (int i = 2;i <= n;++i) {
		cin >> p[i];
		to[p[i]].push_back(i);
	}
	dfs(1, 0);
	memset(rem, -1, sizeof(rem));
	while (q--) {
		int x, y;
		cin >> x >> y;
		cout << calc(x, y) << endl;
	}
	return 0;
}
posted @ 2023-03-20 20:59  DTTTTTTT-  阅读(74)  评论(0)    收藏  举报