返回顶部

Codeforces Round #765 (Div. 2)题解

目前补到C

A. Ancient Civilization

题目描述:给你\(n\)个长度为\(l\)的二进制数组\(a\),这些二进制数以十进制的形式给你,让你求一个最小的\(l\)位二进制数\(x\),定义\(d(x , y)\)表示两个数字二进制表示下不同位的个数,如10011100\(d = 2\)。求最小的\(x\)使和式\(\sum\limits_{i = 1}^{n} d(a_i , x)\)的值最小。

思路:显然将\(a_i\)的每一位拆开,若\(a_i\)的第\(j\)位的数量小于\(n\)的一半,那么对答案的贡献为0 << j否则对答案的贡献是1 << j

时间复杂度:\(O(Tnl)\)

参考代码:

int n, l, a;
void solve() {
	cin >> n >> l;
	vector<int> cnt(l, 0);
	for (int i = 1; i <= n; ++i) {
		cin >> a;
		for (int j = 0; j < l; ++j) cnt[j] += a >> j & 1;
	}
	int res = 0;
	for (int i = 0; i < l; ++i) {
		int dx = 2 * cnt[i] > n;
		res |= dx << i;
	}
	cout << res << '\n';
	return;
}

B. Elementary Particles

题目描述:给你一个长度为\(n\)的数组\(a\),求最长的整数\(k\),使得数组\(a\)存在两个不同的长度为\(k\)的子段\(b , c\),且\(\exists 1 \leq i \leq k\),使得\(b_i = c_i\)

数据范围:\(1 \leq T \leq 100 , 2 \leq n \leq 1.5 \times 10^5 ,1 \leq a_i \leq 1.5 \times 10^5, \sum n \leq 3 \times 10^5\)

思路:考虑到只需要存在即可,我们可以将相同数字的下标存下来,然后遍历每一个相同的数字,若当前数字个数小于\(2\),跳过,否则枚举相邻两个相同的数字,假设其下标为\(u , v\),则其向左边最多取\(min(u , v)\)(包含当前下标),向右边最多取\(min(n - u , n - v)\)(不包含当前下标),那么最终的长度就为

\[min(u , v) + min(n - u , n - v) \]

对于所有的取最大值即可。注意为了不重复遍历需要进行去重。

时间复杂度:\(O(Tnlogn)\)

空间复杂度:\(O(n)\)

参考代码:

const int N = 2e5 + 5;
vector<vector<int>>g(N);
int n;
void solve() {
	cin >> n;
	vector<int>a(n + 1, 0);
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		g[a[i]].push_back(i);
	}
	std::sort(a.begin(), a.end());
	int m = unique(a.begin(), a.end()) - a.begin();
	int res = -1;
	for (int i = 1; i < m; ++i) {
		if (g[a[i]].size() < 2) continue;
		int len = g[a[i]].size();
		for (int j = 1; j < len; ++j) {
			int u = g[a[i]][j - 1], v = g[a[i]][j];
			int lr = min(u, v), rs = min(n - v, n - u);
			res = max(res, lr + rs);
		}
	}
	cout << res << '\n';
	for (int i = 1; i < m; ++i) g[a[i]].clear();
	return;
}

C. Road Optimization

题目描述:给你一个长度为\(n\)的数组\(a\),和一个长度为\(n + 1\)的数组\(d\),(注:实际给你的只有\(n\)个,\(d_{n + 1} = l\))。其价值的计算公式为:

\[f = \sum\limits_{i = 1}^{n}(d_{i + 1} - d_{i}) * a_i \]

现让你删除其中的不超过\(k\)个元素,求\(min \{f\}\)

数据范围:\(1 \leq n \leq 500 , 1 \leq l \leq 10^5 , 0 \leq k \leq n - 1 , 1 \leq a_i \leq 10^4\)

思路:比较显然的\(dp\),定义状态\(f_{i , j}\)表示以第\(i\)个数结尾删除\(j\)个数字后所能取得的最小值,则最终答案为:

\[\mathop{min}\limits_{i = 0}^{k} f_{n , i} \]

考虑如何转移,假设枚举\((i , j]\)表示要删除该区间内的所有元素,则此时区间对答案的贡献为\((d_{j + 1} - d_i) * a_i\)。则转移方程为:

\[f_{j, r + j - i} = \mathop{min}\limits_{ \forall 0 \leq r \leq k - j + i}\{f_{j , r + j - i} , f_{i - 1 , r} + (d_{j + 1} - d_i) * a_i\} \]

时间复杂度:\(O(n^2k)\)

参考代码:

int n, l, k;
void solve() {
	cin >> n >> l >> k;
	vector<int>d(n + 2, 0), a(n + 1, 0);
	for (int i = 1; i <= n; ++i) cin >> d[i];
	for (int i = 1; i <= n; ++i) cin >> a[i];
	d[n + 1] = l;
	vector<vector<int>>f(n + 1, vector<int>(k + 1, 0x3f3f3f3f));
	int res = INT_MAX;
	f[0][0] = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = i; j <= n; ++j) {
			int len = j - i;
			for (int r = 0; r + len <= k; ++r) {
				f[j][r + len] = min(f[j][r + len], f[i - 1][r] + (d[j + 1] - d[i]) * a[i]);
			}
		}
	}
	for (int i = 0; i <= k; ++i) res = min(res, f[n][i]);
	cout << res << '\n';
	return;
}
posted @ 2022-01-13 11:33  cherish-lgb  阅读(67)  评论(0编辑  收藏  举报