0
点赞
收藏
分享

微信扫一扫

CF-1660 Codeforces Round #780 (Div. 3)-AK题解

GG_lyf 2022-04-02 阅读 37
算法

题目链接

A

题意

思路

  • 如果没有一元硬币,那么答案就是1
  • 否则答案就是 a + b ∗ 2 + 1 a+b*2+1 a+b2+1

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int a, b;
		cin >> a >> b;
		if(a == 0) cout << 1 << '\n';
		else cout << a + b * 2 + 1 << '\n';
	}

    return 0;
}

B

题意

思路

  • 如果只有一堆
    • 如果这堆只有一个糖果:YES
    • 否则:NO
  • 否则:排序
    • 如果最大数量的那一包和次大的那包相差不超过1:YES(因为可以一直这样削弱数量最多的,直到将全部都削弱掉)
    • 否则:NO

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n;
		int ok = 0;
		cin >> n;
		vector<int> a(n + 1);
		for(int i = 1; i <= n; i++) cin >> a[i];
		sort(a.begin() + 1, a.end());
		if(n == 1 && a[n] == 1) ok = 1;
		else if((a[n] == a[n - 1] || a[n] == a[n - 1] + 1)) ok = 1;
		if(ok) cout << "YES" << '\n';
		else cout << "NO" << '\n';
	}

    return 0;
}

C

题意

思路

  • 贪心
  • 从前向后遍历,直到遇到过一种字符出现过两次,保留出现过两次的这个字符,其他的全都删掉,然后重复这个过程直到结束。

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int maxn = 2e5 + 6;
char s[maxn];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		cin >> s + 1;
		int n = strlen(s + 1), ans = 0;
		bitset<26> cnt(0);
		for(int i = 1; i <= n; i++) {
			if(cnt[s[i] - 'a'] == 0) cnt[s[i] - 'a'] = 1, ans++;
			else {
				ans--;
				cnt.reset();
			}
		}
		cout << ans << '\n';
	}

	return 0;
}

D

题意

思路

  • 保留下来的数组是不能包含有 0 0 0的,所以我们可以用 0 0 0分割该数组,对于每一段分开处理
  • 子段的值最大,说明该子段有很多的“2”,所以我们要保证得到的子段里 2 2 2尽可能的多
    • 如果该子段中负数的数量是偶数个,那么可以不删
    • 否则我们就可以只从一边删元素,直到删到一个负数就停下。

实现方法:

tuple<int, int, int> ans = {-1, 0, 0};
get<0>(ans); //得到ans的第一个元素

保留的三个数分别是区间 ∣ a i ∣ = 2 |a_i|=2 ai=2的数的数量、区间的左端点、区间的右端点,由于 t u p l e tuple tuple已经重载了比较运算符,比较的时候第一个元素的优先级最高,然后是第二个元素……

  • 通过实现pre函数得到只删子段前边元素的最优解
  • 通过实现suf函数得到只删子段后边元素的最优解

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n;
		cin >> n;
		vector<int> a(n + 1), ve(1, 0);
		tuple<int, int, int> ans = {-1, 0, 0};
		for(int i = 1; i <= n; i++) {
			cin >> a[i];
			if(a[i] == 0) ve.push_back(i);
		}
		ve.push_back(n + 1);
		auto pre = [&](int l, int r) -> tuple<int, int, int> {
			tuple<int, int, int> ret = {0, l, r};
			int fu = 0;
			for(int i = l; i <= r; i++) {
				get<0>(ret) += (abs(a[i]) == 2);
				fu += (a[i] < 0);
			}
			if(fu & 1) {
				for(int i = l; i <= r; i++) {
					if(abs(a[i]) == 2) get<0>(ret) -= 1;
					get<1>(ret)++;
					if(a[i] < 0) break;
				}
			}
			return ret;
		};
		auto suf = [&](int l, int r) -> tuple<int, int, int> {
			tuple<int, int, int> ret = {0, l, r};
			int fu = 0;
			for(int i = l; i <= r; i++) {
				get<0>(ret) += (abs(a[i]) == 2);
				fu += (a[i] < 0);
			}
			if(fu & 1) {
				for(int i = r; i >= l; i--) {
					if(abs(a[i]) == 2) get<0>(ret) -= 1;
					get<2>(ret)--;
					if(a[i] < 0) break;
				}
			}
			return ret;
		};
		for(int i = 1; i < ve.size(); i++) {
			ans = max(ans, pre(ve[i - 1] + 1, ve[i] - 1));
			ans = max(ans, suf(ve[i - 1] + 1, ve[i] - 1));
		}
		cout << get<1>(ans) - 1 << ' ' << n - get<2>(ans) << '\n';
	}

    return 0;
}

E

题意

思路

不难发现,对这个数组只需要一种循环移位操作就可以得到最优的答案,因为主对角线上的值不管选用哪种循环移位方法,在n次循环移位过后都会找到最优解。所以将行数扩大一倍,将该01矩阵赋值一边,求一个斜缀和(记录这个点的左上方有多少个0),即可在 O ( n 2 ) O(n^2) O(n2)的复杂度下得到最优答案。

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int maxn = 2e3 + 5;
char s[maxn << 1][maxn];
int pre[maxn << 1][maxn];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n, t0 = 0, t1 = 0;
		cin >> n;
		for(int i = 1; i <= n; i++) {
			cin >> s[i] + 1;
		}
		for(int i = n + 1; i <= n << 1; i++) {
			for(int j = 1; j <= n; j++) {
				s[i][j] = s[i - n][j];
				t0 += (s[i][j] == '0');
			}
		}
		t1 = n * n - t0;
		for(int i = 1; i <= n << 1; i++) {
			for(int j = 1; j <= n; j++) {
				pre[i][j] = pre[i - 1][j - 1] + (s[i][j] == '0');
			}
		}
		int ans = 0x3f3f3f3f;
		for(int i = n + 1; i <= n << 1; i++) {
			ans = min(ans, pre[i][n] + t1 - (n - pre[i][n]));
		}
		cout << ans << '\n';
	}

    return 0;
}

F1

题意

思路

两个减号可以被替换为一个加号,如果我们定义一个前缀和,减号代表减一,加号代表加一,通过一次操作过后字符串的值比原来多了3

  • 如何确定一个串中是否存在两个连续的减号呢?如果该字串的值是小于-2的,那么必然存在(细品)
  • 如何确定串是有希望变好呢?当且仅当串的值小于等于零,并且串的值对三取余的值为零。
  • F1数据范围较小, O ( n 2 ) O(n^2) O(n2)暴力即可;

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int maxn = 3e3 + 5;
char s[maxn];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n, ans = 0;
		cin >> n >> s + 1;
		vector<int> pre(n + 1);
		for(int i = 1; i <= n; i++) {
			if(s[i] == '-') pre[i] = pre[i - 1] - 1;
			if(s[i] == '+') pre[i] = pre[i - 1] + 1;
		}
		for(int i = 1; i <= n; i++) {
			for(int j = i + 1; j <= n; j++) {
				int sum = pre[j] - pre[i - 1];
				if(sum % 3 == 0 && sum <= 0) {
					ans += 1;
				}
			}
		}
		cout << ans << '\n';
	}

    return 0;
}

F2

题意

见F1,数据范围扩大至2e5.

思路

  • 很显然 O ( n 2 ) O(n^2) O(n2)的复杂度不能满足本题要求,那么什么样的子串才符合模三余零呢,我们把前缀和值为0 3 6 9……、前缀和值为1 4 7 10……、前缀和值为2 5 8 11……的这些区间单独拿出来考虑,因为前缀和上两个数做差可以得到一个子串,所以我们可以利用类似于树状数组求逆序对的方法,开三个树状数组,对于每个位置都算一个贡献,这个贡献就算比当前值小的并且和当前数同余的值的数量,用树状数组可以很简单的维护,复杂度 O ( n l o g n ) O(nlogn) O(nlogn).

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int maxn = 2e5 + 7;
char s[maxn];

template<typename T>
struct Fenwick {
	int n;
	vector<T> ft;
	Fenwick(int n_ = 0) : n(n_), ft(n_ + 1) {}
	void add(int i, int val) {
		while (i < n) {
			ft[i] += val;
			i |= i + 1;
		}
	}
	T query(int i) {
		T x = 0;
		while (i >= 0) {
			x += ft[i];
			i &= i + 1, i--;
		}
		return x;
	}
};

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n;
		ll ans = 0;
		cin >> n >> s + 1;
		vector<int> pre(n + 1, 0);
		for(int i = 1; i <= n; i++) {
			if(s[i] == '+') pre[i] = pre[i - 1] - 1;	
			if(s[i] == '-') pre[i] = pre[i - 1] + 1;	
		}
		Fenwick<int> bit[3];
		for(int i = 0; i <= 2; i++) bit[i] = Fenwick<int>(n * 2 + 5);
		for(int i = 0; i <= n; i++) {
			ans += bit[(pre[i] % 3 + 3) % 3].query(pre[i] + n);
			bit[(pre[i] % 3 + 3) % 3].add(pre[i] + n, 1);
		}
		cout << ans << '\n';
	}

	return 0;
}

结语

喜欢算法竞赛,喜欢它带给我的快乐,乐亦在其中矣。希望可以在昆明取得好成绩。

举报

相关推荐

0 条评论