CF系列题解
A. Tokitsukaze and All Zero Sequence
给定一个序列,可以进行如下操作:
问将所有数变为 0 0 0 所需要的最少操作次数。
分类讨论:
实现上用了排序。
#include
using namespace std;
using i64 = long long;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i ++ ) cin >> a[i];
sort(a.begin(), a.end());
if (a[0] == 0) {
int i = 0;
while (i < n && a[i] == 0) i ++ ;
cout << n - i << "\n";
} else {
bool flag = false;
for (int i = 0; i < n - 1; i ++ ) {
if (a[i] == a[i + 1]) {
flag = true;
break;
}
}
if (flag) {
cout << n << "\n";
} else {
cout << n + 1 << "\n";
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
B1. Tokitsukaze and Good 01-String (easy version)
给定一个 01
字符串,倘若每个连续的相同(即全为 0
或全为 1
)的段其长度为偶数,我们则认为其是好的。问将一个字符串变好需要的最少操作次数。
由于要求每段长度是偶数,那么(下标从 0 0 0 开始)应该满足 s 0 = s 1 , s 2 = s 3 , . . . s_0=s_1,s_2=s_3,... s0=s1,s2=s3,...,对于不满足的情况我们需要改变其中一个,因此 ans++
。
#include
using namespace std;
using i64 = long long;
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
int ans = 0;
for (int i = 0; i + 1 < n; i += 2) {
if (s[i] != s[i + 1]) {
ans ++ ;
}
}
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
B2. Tokitsukaze and Good 01-String (hard version)
上一题的扩展,在满足最小操作次数的同时要使得段数尽可能少,问操作次数和最小的段数。
比赛又写了一大坨代码,回头理一理思路再写。
#include
using namespace std;
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
int ans0 = 0, res0 = 0;
bool flag = false;
vector<int> id(n / 2);
for (int i = 0; i < s.size(); i ++ ) {
if(i == 0) {
if(s[i] == '0') flag = true;
res0 ++;
} else {
if(flag == true) {
if(s[i] == '1' && (res0 & 1)) {
s[i] = '0';
ans0 ++;
res0 ++;
id[i /2] = 2;
} else if(s[i] == '1' && res0 % 2 == 0) {
flag = false;
res0 = 1;
} else {
res0 ++;
}
} else if(flag == false) {
if(s[i] == '0' && (res0 & 1)) {
s[i] = '1';
ans0 ++;
res0 ++;
id[i / 2] = 2;
} else if(s[i] == '0' && res0 % 2 == 0) {
flag = true;
res0 = 1;
} else {
res0 ++;
}
}
}
}
n = id.size();
for (int i = 0; i < s.size(); i += 2) {
if (id[i / 2] != 2) {
if (s[i] == '1') id[i / 2] = 1;
else id[i / 2] = 0;
}
}
int la = -1, res = 1, res1 = 1, res2 = 1;
if(id[0] == 2) {
id[0] = 1;
la = 1;
for(int i = 1; i < n; i ++ ) {
if(id[i] == 2 || id[i] == la) {
continue;
} else {
la = id[i];
res1 ++;
}
}
id[0] = 0;
la = 0;
for(int i = 1; i < n; i ++ ) {
if(id[i] == 2 || id[i] == la) {
continue;
} else {
la = id[i];
res2 ++;
}
}
res = min(res1, res2);
} else {
for(int i = 0; i < n; i ++ ) {
if(la == -1) {
la = id[i];
continue;
} else {
if(id[i] == 2 || id[i] == la) {
continue;
} else {
la = id[i];
res ++;
}
}
}
}
cout << ans0 << ' ' << res << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}
C. Tokitsukaze and Strange Inequality
给定排列 p p p,求满足 p a < p c , p b > p d p_a
由数据范围可知复杂度 O ( n 2 ) O(n^2) O(n2),因此肯定是枚举,我们可以枚举 b , c b, c b,c, a a a 即为 b b b 的左边比 c c c 小的数, d d d 即为 c c c 的右边比 b b b 小的数,然后就发现很像树状数组求逆序对啊!!!
考虑一下复杂度, l o g ( 5000 ) log(5000) log(5000) 也就是个常数,而且1.5s时限,加上cf神机,虽然好像时间有点紧,但应该能过。
(带火们好像大多用的是前缀和呜呜呜,太菜了想不到)
#include
using namespace std;
using i64 = long long;
template <typename T>
struct Fenwick {
const int n;
vector<T> tr;
Fenwick(int n) : n(n), tr(n) {}
int lowbit(int x) {
return x & -x;
}
void add(int x, T v) {
for (int i = x; i < n; i += lowbit(i)) tr[i] += v;
}
T query(int x) {
T res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
T rangeSum(int l, int r) {
return query(r) - query(l - 1);
}
};
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
Fenwick<int> fen1(n + 1), fen2(n + 1);
i64 ans = 0;
for (int b = 2; b <= n - 2; b ++ ) {
fen1.add(a[b - 1], 1);
for (int c = n - 1; c > b; c -- ) {
fen2.add(a[c + 1], 1);
ans += fen2.query(a[b] - 1) * fen1.query(a[c] - 1);
}
for (int c = n - 1; c > b; c -- ) fen2.add(a[c + 1], -1); // 注意还原
}
cout << ans << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T -- ) {
solve();
}
return 0;
}