【比赛】ARC136|220227
A - A ↔ BB
考虑一些贪心的事实:
- 如果有连着的
BB
,换成A
显然更优 - 如果有连着的
BA
,可以换成AB
更优 - 如果连着的是其他字符,不管最优
#include <bits/stdc++.h>
using namespace std;
int n;
deque<char> s;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n;
char c;
for(int i = 1; i <= n; i++) {
cin >> c;
s.push_back(c);
}
while(!s.empty()) {
if(s.size() > 1) {
char p = *s.begin(), q = *(next(s.begin()));
if(p == 'B' && q == 'A') {
s.pop_front(), s.pop_front();
s.push_front('B'), s.push_front('A');
} else if(p == 'B' && q == 'B') {
s.pop_front(), s.pop_front();
s.push_front('A');
}
}
cout << s.front();
s.pop_front();
}
return 0;
}
B - Triple Shift
Des
给定长度为 \(n\) 的数组 \(A\) 和 \(B\),每次可以选定 \(A\) 中连续的三个数,然后把三个数 \((x,y,z)\) 换为 \((z,x,y)\)。问能不能经过若干次操作把 \(A\) 成 \(B\).
Sol
怎么又是这种交换 + 逆序对题,USACO 铜组才考。
发现是把一个数隔着前面的一个数往前调(三元组前调?),调换之后一定不会改变逆序对的奇偶性。所以直接统计逆序对个数,奇数就不行,偶数就行。
但是你会发现有相等的元素。如果越过了一个相等的元素,那么逆序对个数的奇偶性可能会改变?考虑给相等的元素强行定一个排名,然后如果逆序对个数是奇数的话,就交换两个相等的元素,逆序对个数就会变成偶数。所以只要存在相等的元素就一定有解。
My Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int n, p[N], oa[N];
struct wtf {
int v, id;
} a[N], b[N];
int main() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i].v, a[i].id = i, oa[i] = a[i].v;
for(int i = 1; i <= n; i++) cin >> b[i].v, b[i].id = i;
auto cmp = [&](wtf x, wtf y) {
return x.v < y.v;
};
sort(a + 1, a + n + 1, cmp);
sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i++) {
if(a[i].v != b[i].v) {
cout << "No\n";
return 0;
}
}
for(int i = 1; i <= n; i++)
p[a[i].id] = b[i].id;
int tot = 0;
for(int i = 1; i <= n; i++) {
debug(i, p[i]);
for(int j = i + 1; j <= n; j++) {
if(oa[i] == oa[j]) {
cout << "Yes\n";
return 0;
}
if(p[i] > p[j]) ++tot;
}
}
if(tot & 1) cout << "No\n";
else cout << "Yes\n";
return 0;
}
C - Circular Addition
Des
环上排着 \(n\) 个数 \(a_i\),每次可以选择任意一个区间,把区间内的数都减一。问把所有数都减成 \(0\) 需要的最小操作次数。
\(n\le 2\times 10^5\)
Sol
考场上想了个把积木大赛 套进去的假做法,亏惨了。
正解比较妙,不如考场多弄几个样例猜结论。
考虑差分数组 \(b_i=a_i-a_{(i-1+n)\bmod\ n}\)(\(a\) 数组下标从 0 开始),然后设 \(M=\max a_i\),\(D=\sum b_i[b_i>0]\) 那么答案等于 \(\max(M,D)\)。
首先答案至少需要这个数量的操作,因为每次最多把最大值减少一,\(\sum b_i\) 减少二,而 \(\sum b_i[b_i>0]=\frac {\sum b_i}{2}\).
然后证可以在这个数量内操作完毕。
首先考虑 \(M>D\) 的情况,可以证明的是这种情况下不存在 \(a_i=0\),因为考虑到 \(0,\dots,M,\dots,0\) 的这个环会使得 \(M\le D\),矛盾了。于是就可以不断给所有数减一直到 \(M=D\).
然后考虑 \(M<D\) 的情况。每次可以选择一个「最大的」包含 \(M\) 的区间,把这个区间减一(这里没看懂怎么就能一直给 \(D\) 减一了,但是可以考虑每次选择一个区间,保证这个区间外面左边和右边的数分别比区间里面左边和右边的数小,应该是能选到的)。
最后考虑 \(M= D\) 的情况。如果只有一个 \(M\),选择最大的包含 \(M\) 的区间减一就能让 \(M,D\) 同时减一。如果有多个 \(M\),则一定不存在 \(a_i=0\) 的情况(考虑 \(0,\dots,M\dots,0\) 仍然可以得证)。所以可以选择一个最大的包含所有 \(M\) 的区间减一,也能让 \(M,D\) 同时减一。
这样就完成证明了,时间复杂度 \(O(n)\).
My Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int n;
ll ans;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n;
ll M = 0, D = 0;
vector<int> a;
for(int i = 1, t; i <= n; i++) {
cin >> t;
a.emplace_back(t);
M = max(M, (ll)t);
}
for(int i = 0; i < (int)a.size(); i++)
D += abs(a[i] - a[(i - 1 + n) % n]);
D /= 2;
cout << max(M, D) << '\n';
return 0;
}
D - Without Carry
Des
给定 \(n\le 10^6\) 个数,求使得 \(a_i+a_j\) 做竖式加法不进位,且 \(i<j\) 成立的二元组 \((i,j)\) 的个数。
Sol
考场上觉得比 C 题简单但是不会做,以为是容斥结果不行。然后想到可能是一个多维的前缀和,但是不会构建,卒。
可以看一下 CF 讨论里某好心人教的高维前缀和构建方法 。我就不写了。
设 \(L=\log_{10}V\),复杂度是 \(O(nL+L\cdot 10^L)\)。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for(int i = j; i <= k; i++)
const int N = 1e6 + 5;
int n, a[N], p[10];
int s[11][11][11][11][11][11];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
for(int x = a[i], j = 1; j <= 6; j++, x /= 10) p[j] = x % 10 + 1;
++s[p[1]][p[2]][p[3]][p[4]][p[5]][p[6]];
}
rep(w, 1, 6)
rep(i1, 1, 10)
rep(i2, 1, 10)
rep(i3, 1, 10)
rep(i4, 1, 10)
rep(i5, 1, 10)
rep(i6, 1, 10)
s[i1][i2][i3][i4][i5][i6] += s[i1 - (w == 1)][i2 - (w == 2)][i3 - (w == 3)][i4 - (w == 4)][i5 - (w == 5)][i6 - (w == 6)];
long long ans = 0;
for(int i = 1; i <= n; i++) {
bool flag = true;
for(int x = a[i], j = 1; j <= 6; j++, x /= 10) p[j] = x % 10, flag &= (p[j] < 5);
ans += s[10 - p[1]][10 - p[2]][10 - p[3]][10 - p[4]][10 - p[5]][10 - p[6]];
ans -= flag;
}
ans /= 2;
cout << ans << '\n';
return 0;
}