Codeforces Round 875 (Div. 2) 题解 A - D
A. Twin Permutations
题目大意
题目给定一个 \(1\sim n\) 的排列 \(a\) ,现在想求一个排列 \(b\), 使得对于 \(i < j\) 都有 \(a_i + b _i \le a_j + b_j\) 。
解题思路
我们发现,题目允许我们取到等于号,这样我们就一定可以构造使得每一组数之和相同,都为 \(n + 1\),只需要用 \(n + 1- a_i\) 当作 \(b_i\) 加上 \(a_i\) 即可。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
cout << n - x + 1 << ' ';
}
cout << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
B. Array merging
题目大意
给定两个长度都为 \(n\) 的数组 \(a\) 和 \(b\), 现在要将他俩合并成一个数组,求新数组中最长连续相同元素数量是多少。
解题思路
考虑先分后和。
对于两个数组,我们可以先分别求出他们各自最大连续元素的数量,对于每一种元素分别计数,用一个数组存起来。由于题目约束数组元素都在 \([1,2n]\) 中,我们很容易可以做到这一点。接下来只需要在 \([0,2n]\) 中遍历数组,找到两数组中,相同元素最长连续元素之和的最大值之和。注意这里的说法很绕,可以参考下面的代码辅助理解。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
int a[N], b[N];
void solve() {
int n;
cin >> n;
vector<int> c1(n * 2 + 1), c2(n * 2 + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= n; ++i) {
cin >> b[i];
}
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; ++i) {
if (a[i] != a[i - 1]) {
c1[a[i - 1]] = max(c1[a[i - 1]], cnt1);
cnt1 = 1;
} else {
cnt1++;
}
if (b[i] != b[i - 1]) {
c2[b[i - 1]] = max(c2[b[i - 1]], cnt2);
cnt2 = 1;
} else {
cnt2++;
}
}
c1[a[n]] = max(c1[a[n]], cnt1);
c2[b[n]] = max(c2[b[n]], cnt2);
LL res = 0;
for (int i = 1; i <= (n << 1); ++i) {
res = max(res, (LL)c1[i] + c2[i]);
}
cout << res << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
C. Copil Copac Draws Trees
题目大意
给定一个 \(n\) 结点,\(n - 1\) 条边的无向图,现在我们要按照题目给出的顺序画出这个无向图,起初图上只有 \(1\) 这个结点,如果遇到两个结点都不在图上的情况,就跳过这一行,如果有一个结点在图上,就标出另一个点,并画出这条无向边,直到遍历结束,从头再次遍历,反复操作一直到无向图创建完毕。问需要反复遍历几遍。
解题思路
首先我们可以知道最多操作的次数就是 \(n-1\) ,也就是逆序给出每个结点的情况。对于这一题,每条无向边给出的顺序有了很大的意义,那么我们可以在使用邻接表存边的时候把这是第几条建边指令也存进去。随后从第一个点开始dfs搜索我们这张图,每个点在第cnt[i]巡被创建,那么转移方程很显然是cnt[u] + (fore[i] < id)
,这里的u指的是找到的连在他身上的另外一个点,布尔语句做值代表着如果满足后创立的点在先创立的点之前出现,我们计数值就必须加一。因为dfs只是遍历了所有点,所以复杂度为 \(O(n)\) 绰绰有余,
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 4e5 + 10;
const int MOD = 1e9 + 7;
int h[N], e[N], ne[N], idx, fore[N];
LL cnt[N];
bool vis[N];
void add(int a, int b, int id) {
e[idx] = b;
ne[idx] = h[a];
fore[idx] = id;
h[a] = idx++;
}
void dfs(int u, int id) {
vis[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
if (!vis[e[i]]) {
cnt[e[i]] = cnt[u] + (fore[i] < id);
dfs(e[i], fore[i]);
}
}
}
void solve() {
int n;
cin >> n;
idx = 0;
memset(h, -1, sizeof h);
memset(vis, 0, sizeof vis);
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
add(u, v, i);
add(v, u, i);
}
dfs(1, 0x3f3f3f3f);
cout << *max_element(cnt + 1, cnt + n + 1) << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
D. The BOSS Can Count Pairs
题目大意
给定两个长度均为 \(n\) 的数组 \(a\) 和 \(b\),我们要找到两个数组中,满足 \(a_i \times a_j == b_i + b_j\) 的无序数对组数。
解题思路
和B题一样,数据范围有压制,本题的元素都在 \([1,n]\) 中,所以 \(a_i \times a_j = b_i + b_j \le 2n\) ,有这两个限制,可以考虑根号分治的做法。由于本题只绑定了两个数组,他们的先后顺序并没有限制,所以可以先对 \(a\) 数组排序。这样我们可以选取两个元素 \(i<j\) 同时有 \(a_i < a_j\)。在这种情况下根号分治枚举,我们只需要去枚举 \(i < \sqrt{2n}\) 的部分,再遍历整个数组,得到另外一组 \((a_j,b_j)\),这样,使用 \((a_j,b_j)\) 和枚举出的 \(a_i\) ,可以计算出 \(b_i\) 的值,只需要检验这个 \((a_i,b_i)\) 是否匹配即可。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
int cnt[N];
pair<int, int> p[N];
void solve() {
int n;
cin >> n;
LL res = 0;
for (int i = 1; i <= n; ++i) {
cin >> p[i].first;
}
for (int i = 1; i <= n; ++i) {
cin >> p[i].second;
}
sort(p + 1, p + n + 1);
for (int i = 1; i * i <= (n << 1); ++i) {
memset(cnt, 0, sizeof cnt);
for (int j = 1; j <= n; ++j) {
int cal = p[j].first * i - p[j].second;
if (cal >= 1 && cal <= n) {
res += cnt[cal];
}
if (p[j].first == i) {
cnt[p[j].second]++;
}
}
}
cout << res << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}