VP Codeforces Round 906 (Div. 2)
A. Doremy's Paint 3
题意:给你你个数组,你要重排它使得每两个相邻数的和都相同。
\(a_1 + a_2 = a_2 + a_3\),那么\(a_1 = a_3\),\(a_2 + a_3 = a_3 + a_4\),那么\(a_2 = a_4\),然后发现奇数位置都相等,偶数位置都相等。那么数组只有一类数明显可以,如果有两类数,判断是不是有各占\(\frac{n}{2}\)个即可,注意\(n\)为奇数的情况有一类数会多占一个。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::set<int> s;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
s.insert(a[i]);
}
int cnt = 0;
for (int i = 0; i < n; ++ i) {
cnt += a[i] == a[0];
}
if ((s.size() == 2 && (cnt == n / 2 || cnt == (n + 1) / 2)) || s.size() == 1) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
B. Qingshan Loves Strings
题意:给你两个\(01\)串\(s, t\),你每次可以把\(t\)插到\(s\)的任意位置,求能不能使得\(s\)相邻的数都不相同。
如果\(s\)本来不满足条件并且\(t\)也不满足条件,则无解。否则考虑插入\(t\),如果\(s_i == s_{i+1}\),那么则需要插入\(t\),判断\(t\)插入后是不是满足要求。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::string s, t;
std::cin >> s >> t;
bool flag = true;
for (int i = 1; i < n; ++ i) {
flag &= s[i] != s[i - 1];
}
if (flag) {
std::cout << "YES\n";
return;
}
flag = true;
for (int i = 1; i < m; ++ i) {
flag &= t[i] != t[i - 1];
}
if (flag) {
for (int i = 1; i < n; ++ i) {
if (s[i] == s[i - 1]) {
if (s[i - 1] == t[0] || s[i] == t.back()) {
std::cout << "NO\n";
return;
}
}
}
std::cout << "YES\n";
return;
}
std::cout << "NO\n";
}
C. Qingshan Loves Strings 2
题意:给你一个\(01\)串\(s\),你可以在任意位置插入\(01\),设最后\(s\)的长度为\(k\),求能不能使得最后所有的\(i \in [1, k], s_i != s_{n-i+1}\)。
首先每次插入都会使得\(0\)和\(1\)的数量加一,这不影响它们的差,那么一开始\(0\)和\(1\)的个数就要相等。
假设\([1, i - 1], [n - i + 2, n]\)都已经满足要求,那么如果\(s_i == s_{n-i+1}\),当\(s_i == '0'\),则在\(s_{n-i+1}\)的后面插入\(01\) ,否则在\(i\)的前面插入\(01\),发现这样操作最后一定满足要求。暴力模拟即可。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
int cnt[2]{};
for (auto & c : s) {
++ cnt[c - '0'];
}
if (cnt[0] != cnt[1]) {
std::cout << -1 << "\n";
return;
}
std::vector<int> ans;
auto insert = [&](int p) -> void {
ans.push_back(p);
s = s.substr(0, p + 1) + "01" + s.substr(p + 1);
};
for (int l = 0, r = n - 1; l < r; ++ l, -- r) {
if (s[l] == s[r]) {
if (s[l] == '0') {
insert(r);
r += 2;
} else {
insert(l - 1);
r += 2;
}
}
}
std::cout << ans.size() << "\n";
for (auto & p : ans) {
std::cout << p + 1 << " \n"[p == ans.back()];
}
}
D. Doremy's Connecting Plan
题意:一开始有\(n\)个集合,第\(i\)个集合的值为\(a_i\)。你每次选择两个位置\(i, j\),设\(i\)所在集合的值为\(S_i\),\(j\)所在集合的值为\(S_j\)。那么如果\(S_i + S_j \geq i \times j \times c\)则可以合并这两个集合,新集合的值为\(S_a + S_b\)。求能不能合并成一个集合。
我们一定是选两个集合最小的两个位置操作,因为这样\(i \times j\)最小,那么发现,我们一开始一定可以选\(1\)和某个位置操作,利用反证法,假设一开始选择\((i, j)\)进行操作\(i \ne 1, j \ne 1\),并且\((1, i)\)和\((1, j)\)都无法操作。那么有\(a_i + a_j \geq i \times j \times c\),\(a_i + a_1 < 1 \times i \times c\), \(a_j + a_1 < 1 \times j \times c\), 那么得到\(a_i + a_j + 2a_i < (i + j) \times c\),由于\(i \ne 1, j \ne 1\),那么\(i \times j \geq i + j\),于是产生矛盾。
那么我们现在改一下式子:\(S_i + S_j \geq i \times j \times c ==> S_i \geq i \times j \times c - S_j\),根据贪心策略,我们一定是每次选\(1\)和其它位置操作,那么式子就是\(S_1 \geq j \times c - S_j\),于是按\(j \times c - S_j\)排序,从小到大合并。
点击查看代码
void solve() {
int n;
i64 c;
std::cin >> n >> c;
std::vector<std::array<i64, 3>> a(n + 1);
for (int i = 1; i <= n; ++ i) {
i64 x;
std::cin >> x;
a[i] = {i * c - x, x, i};
}
std::sort(a.begin() + 2, a.end());
i64 sum = a[1][1];
for (int i = 2; i <= n; ++ i) {
if (sum < a[i][0]) {
std::cout << "NO\n";
return;
}
sum += a[i][1];
}
std::cout << "YES\n";
}
E1. Doremy's Drying Plan (Easy Version)
题意:有\(n\)个线段,你要删除其中两个,使得没有被线段覆盖过的点最多。
分成两种情况:
- 两个线段不相交
- 两个线段相交
我们先利用差分算出每个点被多少线段覆盖。
对于第一种情况,我们记录线段覆盖的区间中只被\(1\)个线段覆盖的点的最大值和次大值。如果最终答案为不相交的两个线段,那么显然求出来的就是答案,否则最终答案为两个相交的线段,则求出来的值一定小于第二种情况的值。
对于第二种情况,我们用优先队列存每个线段的右端点和左端点,先把线段存在数组里按左端点排序,然后从小到大枚举,对于每个左端点为\(i\)的线段都加进来,然后对于每个前面加进来的线段的右端点小于\(i\)的线段都删去,那么就得到了每个点被覆盖的线段。如果这个点恰好被两个线段覆盖,那么就取出来更新答案。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
using PII = std::pair<int, int>;
std::vector<PII> segs;
std::vector<int> d(n + 2);
for (int i = 0; i < m; ++ i) {
int l, r;
std::cin >> l >> r;
segs.push_back({l, r});
d[l] += 1;
d[r + 1] -= 1;
}
std::vector<int> one(n + 1), two(n + 1);
int ans = 0;
for (int i = 1; i <= n; ++ i) {
d[i] += d[i - 1];
one[i] = one[i - 1];
two[i] = two[i - 1];
if (d[i] == 0) {
++ ans;
} else if (d[i] == 1) {
++ one[i];
} else if (d[i] == 2) {
++ two[i];
}
}
int max1 = 0, max2 = 0;
for (auto & [l, r] : segs) {
int cnt = one[r] - one[l - 1];
if (cnt >= max1) {
max2 = max1;
max1 = cnt;
} else if (cnt > max2) {
max2 = cnt;
}
}
int ans1 = max1 + max2;
std::sort(segs.begin(), segs.end());
std::priority_queue<PII, std::vector<PII>, std::greater<>> heap;
for (int i = 1, t = 0; i <= n; ++ i) {
while (heap.size() && heap.top().first < i) {
heap.pop();
}
while (t < m && segs[t].first == i) {
heap.push({segs[t].second, segs[t].first});
++ t;
}
if (heap.size() == 2) {
auto [a1, a2] = heap.top(); heap.pop();
auto [a3, a4] = heap.top();
std::vector<int> a{a1, a2, a3, a4};
std::sort(a.begin(), a.end());
int cnt = one[a[3]] - one[a[0] - 1] + two[a[2]] - two[a[1] - 1];
ans1 = std::max(ans1, cnt);
heap.push({a1, a2});
}
}
std::cout << ans + ans1 << "\n";
}

浙公网安备 33010602011771号