Codeforces Round #764 (Div. 3)题解
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\),一次操作可以选择数组中的元素,然后将其修改为\(\lfloor \frac{a_i}{2}\rfloor\)。问你是否可以通过操作将数组变成一个\(1\sim n\)的排列,是输出YES
,否则输出NO
。
思路:显然若存在,则最终答案的数字是确定的,我们可以直接枚举每一个数组中的元素,然后若当前元素在\(1\sim n\)且前面没有出现过,就将该位置标记,否则一直除以\(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 \times m \leq 10^6\)。我们可以暴力的将这\(n\)个串的长度为\(2\)和长度为\(3\)的子串处理出来然后用map
统计,考虑到这些字符串只含有数字,所以将这些子串表示成相对应的十进制数,若存在冲突,使用一些简单的方式解决一下即可,当然也可以以使用桶替换map
,然后就是愉快的\(dp\)求可行性即可,最后使用dfs
求出分割方案即可。
时间复杂度:\(n \times m log (n\times 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 , 1\leq x < n\)。交互方式为你给定\(c\),然后\(x = x + c\),并告知你\(\lfloor \frac{x}{n}\rfloor\)。要求次数不超过\(10\)。\(1 \leq n \leq 1000\)。
思路:设\(y = x , t = \sum c\),则(注:下述的除法都是向下取整除法)
若\(\frac{x}{n} = \frac{t}{n} + 1\),其中\(x = y + t\),则
若\(\frac{x}{n} = \frac{t}{n}\),则
所以我们考虑二分答案求\(y\),每次二分出来的是\(mid\),但实际的\(y\)是\(mid + 1\),那么我们的目标就是让自己枚举出来的\(y\)加上\(t\)恰好是\(n\)的倍数。每次询问的是\(y + \sum 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 | mid \leq mid\)则将这条边加入并查集中,最后检验选出的这些边是否能构成一个连通图,若能就更新答案并\(rs= mid - 1\),若不能就\(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;
}
作者:cherish.
出处:https://home.cnblogs.com/u/cherish-/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。