返回顶部

Codeforces Round #764 (Div. 3)题解

作者:@cherish.
课程学习内容为作者从学校的PPT处摘抄,仅供自己学习参考,若需转载请注明出处:https://www.cnblogs.com/cherish-/p/15787617.html


Update:2022/1/11 19:57 全部补完了

回家的时候太晚了,没打成 QAQ

A. Plus One on the Subset

题目描述:给你一个长度为n的数组A,求将数组A中的所有元素变相等的最小操作次数,一次操作可以选择任意多个下标的元素,然后对其加一。

思路:显然答案为最大值和最小值的差值。

时间复杂度:O(Tn)

参考代码:

void solve() {
	cin >> n;
	int a, mx(0), mn(INT_MAX);
	for (int i = 1; i <= n; ++i) {
		cin >> a;
		mx = max(mx, a);
		mn = min(mn, a);
	}
	cout << mx - mn << '\n';
	return;
}

B. Make AP

题目描述:给你一个长度为3的数组A,你需要判断是否存在一个正整数m,使得其乘以数组中的某一元素后的新数组是一个等差数列,比如[1,4,6]m=2,然后新数组可以是[2,4,6],此时这个数组是等差数列,满足题解。若存在就输出YES,否则输出NO

思路:因为数组长度为3,所以我们可以枚举那个变化的数字,然后根据另外两个不变的数字确定公差然后进行验证即可。

时间复杂度:O(T)

参考代码:

long long a, b, c;
bool check(long long target, long long val) {
	if (target % val != 0 || target / val <= 0) return false;
	cout << "YES" << '\n';
	return true;
}
void solve() {
	cin >> a >> b >> c;
	//讨论那两个数不变即可
	//b c 不变
	long long dx = c - b;
	if (check(b - dx, a)) return;
	//a c 不变
	dx = c - a;
	if (dx % 2 == 0 && check(c - dx / 2, b)) return;
	// a b 不变
	dx = b - a;
	if (check(b + dx, c)) return;
	cout << "NO" << '\n';
	return;
}

C. Division by Two and Permutation

题目描述:给你一个长度为n的数组A,一次操作可以选择数组中的元素,然后将其修改为ai2。问你是否可以通过操作将数组变成一个1n的排列,是输出YES,否则输出NO

思路:显然若存在,则最终答案的数字是确定的,我们可以直接枚举每一个数组中的元素,然后若当前元素在1n且前面没有出现过,就将该位置标记,否则一直除以2,直到该元素为0或者找到一个没有标记的位置。若最终该元素为0输出NO,否则输出YES

时间复杂度:O(nlogn)

参考代码:

int n;
void solve() {
	cin >> n;
	vector<int>a(n + 1, 0), cnt(n + 1, 0);
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) {
		while (a[i] && (a[i] > n || cnt[a[i]])) a[i] /= 2;
		if (a[i] == 0) {
			cout << "NO" << '\n';
			return;
		}
		cnt[a[i]] = 1;
	}
	cout << "YES" << '\n';
	return;
}

D. Palindromes Coloring

题目描述:给你一个长度为n的字符串s和一个整数k,你需要将字符串s中的字符分成k组,但s中的字符不必全部使用完。然后每一组的字符通过重排之后都是一个回文串,求划分后回文串长度的最小的最大值。

思路:比较显然的二分答案,二分最终的最大值,然后去验证即可。注意过程中需要分奇回文和偶回文进行讨论。

时间复杂度:O(n+26logn)

参考代码:

int n, k;
string s;
void solve() {
	cin >> n >> k >> s;
	int lr = 2, rs = n, res = 1;
	vector<int>cnt(27, 0);
	for (auto& c : s) ++cnt[c - 'a'];
	while (lr <= rs) {
		int mid = lr + rs >> 1;
		int ct = 0;//能组成的长度为mid的回文数目
		if (mid & 1) {//奇回文
			int cur = 0, cnt1 = 0, cnt2 = 0;
			//先将长度为mid - 1的偶回文求出来,然后进行调整
			for (auto& c : cnt) {
				cur += (c & 1) ? c - 1 : c;
				cnt1 += cur / (mid - 1);
				cur %= (mid - 1);
				cnt2 += c & 1;//剩余的字符
			}
			cnt2 += cur;
			while (cnt1 > 0) {
				int dx = min(cnt2, cnt1);
				ct += dx;
				cnt2 -= dx;
				cnt1 -= dx;
				--cnt1;
				cnt2 += mid - 1;
			}
		}
		else {//偶回文
			int cur = 0;
			for (auto& c : cnt) {
				cur += (c & 1) ? c - 1 : c;
				ct += cur / mid;
				cur %= mid;
			}
		}
		if (ct >= k) lr = mid + 1, res = mid;
		else rs = mid - 1;
	}
	cout << res << '\n';
	return;
}

E. Masha-forgetful

题目描述:给你n个长度为m的串,再给你一个长度为m的串s,若将s分割成长度至少为2的子串,这些子串若都出现在这n个串的子串中,则输出相应的方案,否则输出-1

思路:因为题目没有限制最小化分割数,所以我们可以将其分割成长度为2和长度为3的串,然后判断,考虑到n×m106。我们可以暴力的将这n个串的长度为2和长度为3的子串处理出来然后用map统计,考虑到这些字符串只含有数字,所以将这些子串表示成相对应的十进制数,若存在冲突,使用一些简单的方式解决一下即可,当然也可以以使用桶替换map,然后就是愉快的dp求可行性即可,最后使用dfs求出分割方案即可。

时间复杂度:n×mlog(n×m)

参考代码:

int n, m;
string s;
struct infor {
	int lr, rs, idx;
	infor(int _lr = 0 , int _rs = 0 , int _idx = 0):lr(_lr) , rs(_rs) , idx(_idx){}
};
void solve() {
	cin >> n >> m;
	map<int, infor>mp;//字符串 出现在第几个字符串 以及区间
	auto fun = [&](int x) {//对于三位数且有前导0的进行处理
		if (x >= 100) return x;
		return x + 1000;
	};
	//两位数有前导0不用管,三位数有前导0转化成四位数
	for (int i = 1; i <= n; ++i) {
		cin >> s;
		for (int j = 0; j < m - 1; ++j) {
			int dx = 0;
			for (int k = j; k <= j + 1; ++k) dx = dx * 10 + s[k] - '0';
			if (mp.count(dx)) continue;
			else mp[dx] = { j + 1 , j + 2 , i };
		}
		for (int j = 0; j < m - 2; ++j) {
			int dx = 0;
			for (int k = j; k <= j + 2; ++k) dx = dx * 10 + s[k] - '0';
			dx = fun(dx);
			if (mp.count(dx)) continue;
			else mp[dx] = { j + 1 , j + 3 , i };
		}
	}
	cin >> s;
	s = ' ' + s;
	if (m == 1) {
		cout << -1 << '\n';
		return;
	}
	vector<vector< int >> f(2, vector<int>(m + 1, -1));
	f[0][0] = 0;
	f[1][0] = 0;
	for (int i = 2; i <= m; ++i) {
		int dx = 0, dy = 0;
		for (int j = i - 1; j <= i; ++j) dx = dx * 10 + s[j] - '0';
		if ((f[0][i - 2] >= 0 || f[1][i - 2] >= 0) && mp.count(dx)) f[0][i] = dx;
		if (i >= 3) {
			for (int j = i - 2; j <= i; ++j) dy = dy * 10 + s[j] - '0';
			dy = fun(dy);
			if ((f[1][i - 3] >= 0 || f[0][i - 3] >= 0) && mp.count(dy)) f[1][i] = dy;
		}
	}
	if (f[0][m] == -1 && f[1][m] == -1) {
		cout << -1 << '\n';
		return;
	}
	vector<infor> res;
	bool flag = false;
	auto dfs = [&](auto dfs, int cur) {
		if (cur == 0) {
			flag = true;
			return;
		}
		for (int i = 0; i <= 1; ++i) {
			if (f[i][cur] == -1) continue;
			res.push_back(mp[f[i][cur]]);
			dfs(dfs, cur - (i == 0 ? 2 : 3));
			if (flag) return;
			res.pop_back();
		}
		return;
	};
	dfs(dfs, m);
	cout << res.size() << '\n';
	reverse(res.begin(), res.end());
	for (auto& [lr, rs, idx] : res) cout << lr << " " << rs << " " << idx << '\n';
	return;
}

F. Interacdive Problem

题目描述:交互题,给你一个整数n,你需要猜出一个整数x,1x<n。交互方式为你给定c,然后x=x+c,并告知你xn。要求次数不超过101n1000

思路:设y=x,t=c,则(注:下述的除法都是向下取整除法)

(1)0<y<n(2)t<y+t<n+t(3)tny+tntn+1

xn=tn+1,其中x=y+t,则

y+tn=tn+1y+tntn+ny+ttt%n+nynt%n

xn=tn,则

xn=tny+tn=tny+ttnny+ttt%nynt%n

所以我们考虑二分答案求y,每次二分出来的是mid,但实际的ymid+1,那么我们的目标就是让自己枚举出来的y加上t恰好是n的倍数。每次询问的是y+c还再需加多少才能变成n的倍数。

时间复杂度:O(logn)

参考代码:

void solve() {
	int n(0);
	cin >> n;
	int lr = 1, rs = n - 1;
	int last = 0, sum = 0;
	auto query = [&](int val) {
		if (val == 0) return last;
		cout << "+ " << val << endl;
		sum += val;
		int cur(0);
		cin >> cur;
		last = cur;
		return cur;
	};
	while (lr < rs) {
		int mid = lr + rs >> 1;
		int add = ((n - mid - sum - 1) % n + n) % n;
		int target = (mid + 1 + sum + add) / n;
		int res = query(add);
		if (res == target) lr = mid + 1;
		else rs = mid; 
	}
	cout << "! " << lr + sum << endl;
	return;
}

G. MinOr Tree

题目描述:给你一个n个点m条边的带权无向图,求其所有的生成树中,所有边权的或值的最小值。

思路:考虑二分答案,二分最终的异或值设其为mid,然后遍历所有边,若w|midmid则将这条边加入并查集中,最后检验选出的这些边是否能构成一个连通图,若能就更新答案并rs=mid1,若不能就lr=mid+1

时间复杂度:O(mlogmlogn)

参考代码:

int n, m;
struct Edge {
	int u, v, w;
	Edge(int _u = 0, int _v = 0, int _w = 0) :u(_u), v(_v), w(_w) {}
};
void solve() {
	cin >> n >> m;
	int u, v, w;
	vector<Edge> edges;
	for (int i = 1; i <= m; ++i) {
		cin >> u >> v >> w;
		edges.push_back({ u , v , w });
	}
	vector<int>father(n + 1, -1);
	auto find = [&](auto f, int x)-> int {
		if (father[x] == -1) return x;
		return father[x] = f(f, father[x]);
	};
	auto Union = [&](int u, int v)->void {
		u = find(find, u);
		v = find(find, v);
		if (u == v) return;
		father[u] = v;
		return;
	};
	int lr = 0, rs = INT_MAX, res = 0;
	while (lr <= rs) {
		int mid = lr + rs >> 1;
		father = vector<int>(n + 1, -1);
		for (auto& [u, v, w] : edges)
			if ((w | mid) <= mid) Union(u, v);
		int cnt = 0;
		for (int i = 1; i <= n; ++i) cnt += father[i] == -1;
		if (cnt == 1) res = mid, rs = mid - 1;
		else lr = mid + 1;
	}
	cout << res << '\n';
	return;
}
posted @   cherish-lgb  阅读(307)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示