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
题目描述:给你一个长度为的数组,求将数组中的所有元素变相等的最小操作次数,一次操作可以选择任意多个下标的元素,然后对其加一。
思路:显然答案为最大值和最小值的差值。
时间复杂度:
参考代码:
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
题目描述:给你一个长度为的数组,你需要判断是否存在一个正整数,使得其乘以数组中的某一元素后的新数组是一个等差数列,比如,,然后新数组可以是,此时这个数组是等差数列,满足题解。若存在就输出YES
,否则输出NO
。
思路:因为数组长度为,所以我们可以枚举那个变化的数字,然后根据另外两个不变的数字确定公差然后进行验证即可。
时间复杂度:
参考代码:
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
题目描述:给你一个长度为的数组,一次操作可以选择数组中的元素,然后将其修改为。问你是否可以通过操作将数组变成一个的排列,是输出YES
,否则输出NO
。
思路:显然若存在,则最终答案的数字是确定的,我们可以直接枚举每一个数组中的元素,然后若当前元素在且前面没有出现过,就将该位置标记,否则一直除以,直到该元素为或者找到一个没有标记的位置。若最终该元素为输出NO
,否则输出YES
。
时间复杂度:
参考代码:
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
题目描述:给你一个长度为的字符串和一个整数,你需要将字符串中的字符分成组,但中的字符不必全部使用完。然后每一组的字符通过重排之后都是一个回文串,求划分后回文串长度的最小的最大值。
思路:比较显然的二分答案,二分最终的最大值,然后去验证即可。注意过程中需要分奇回文和偶回文进行讨论。
时间复杂度:
参考代码:
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
题目描述:给你个长度为的串,再给你一个长度为的串,若将分割成长度至少为的子串,这些子串若都出现在这个串的子串中,则输出相应的方案,否则输出-1
。
思路:因为题目没有限制最小化分割数,所以我们可以将其分割成长度为和长度为的串,然后判断,考虑到。我们可以暴力的将这个串的长度为和长度为的子串处理出来然后用map
统计,考虑到这些字符串只含有数字,所以将这些子串表示成相对应的十进制数,若存在冲突,使用一些简单的方式解决一下即可,当然也可以以使用桶替换map
,然后就是愉快的求可行性即可,最后使用dfs
求出分割方案即可。
时间复杂度:
参考代码:
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
题目描述:交互题,给你一个整数,你需要猜出一个整数。交互方式为你给定,然后,并告知你。要求次数不超过。。
思路:设,则(注:下述的除法都是向下取整除法)
若,其中,则
若,则
所以我们考虑二分答案求,每次二分出来的是,但实际的是,那么我们的目标就是让自己枚举出来的加上恰好是的倍数。每次询问的是还再需加多少才能变成的倍数。
时间复杂度:
参考代码:
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
题目描述:给你一个个点条边的带权无向图,求其所有的生成树中,所有边权的或值的最小值。
思路:考虑二分答案,二分最终的异或值设其为mid
,然后遍历所有边,若则将这条边加入并查集中,最后检验选出的这些边是否能构成一个连通图,若能就更新答案并,若不能就。
时间复杂度:
参考代码:
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;
}
作者:cherish.
出处:https://home.cnblogs.com/u/cherish-/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!