Codeforces Round #645 (Div. 2)解题报告

Codeforces Round #645 (Div. 2)

A.Park Lighting

题目大意

给你一个由n * m个方块组成的小区,每两个方块相交的边视为街道,一个街道上的灯可以照亮与其相邻的方块,问你最少点亮多少个街道的灯可以使所有方块全部亮起

解题思路

一开始想的是n和m根据奇偶分为4种情况,可以A,但是更好的思路是有n * m个方块需要点亮,一盏灯最多可以点亮两个方块,那么答案就是[(n * m) / 2] = (n * m + 1) / 2([]表示向上取整)

AC代码

#include 
using namespace std;
int solve(int n, int m) {
	if (n == 1) return (m + 1) / 2;
	if (m == 1) return (n + 1) / 2;
	return (n & 1) ? (n / 2) * m + (m + 1) / 2 : (n / 2) * m;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n, m;
		cin >> n >> m;
		//cout << max(1, min(solve(n, m), solve(m, n))) << '\n';
		cout << (n * m + 1) / 2 << '\n';
	}
	return 0;
}

B.Maria Breaks the Self-isolation

题目大意

这题太长了,读起来属实费劲,大致意思是有n个人(不包括你自己),每个人有一个数字,你的数字是1,第i个人的数字是ai,每次你可以叫一些人同时到达院子,但是如果叫来的人发现此时院子里的人少于自己的数字(包括你叫来的人在内,但不包括他自己),那么他会马上离开,问你院子里同一时刻最多有多少个人

解题思路

首先能想到的就是将数字排序,优先把数字小的叫到院子里来,然后O(n)遍历一遍,如果a[i] < i,那么我可以将前i个人全部叫到院子里来,答案取满足条件的i的最大值加1(还要包括你自己)即可

AC代码

#include 
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		int Max = 1;
		for (int i = 1; i <= n; ++i) cin >> a[i];
		sort(a + 1, a + n + 1);
		for (int i = 1; i <= n; ++i) {
			if (a[i] <= i) Max = i + 1;
		}
		cout << Max << '\n';
	}
	return 0;
}

C.Celex Update

题目大意

有一个长这样子的矩阵
Codeforces Round #645 (Div. 2)解题报告_第1张图片
每次你只能向下或向右走,问你从(x1, y1)到(x2, y2)有多少种不同的方法,不同是指路径上所有数字之和不同

解题思路

观察发现答案为(x2 - x1) * (y2 - y1) + 1

AC代码

#include 
using namespace std;
typedef long long ll;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		ll a, b, c, d;
		cin >> a >> b >> c >> d;
		cout << abs((c - a) * (d - b)) + 1 << '\n';
	}
	return 0;
}

D.The Best Vacation

题目大意

假设一年有n个月,每个月有ai天,每个月的第i天你可以得到i快乐值,问你连续x天最大的快乐值是多少(可以跨年)

解题思路

前提:将a数组延长一倍以解决跨年问题。
首先,这x天要么以某一整个月为结尾,要么是某个月的结尾,比如3 2,x = 4,那么1 + 2 + 3 + 1显然小于2 + 3 + 1 + 2,因为你用后一个月的末尾覆盖了前一个月的开头,那么其值肯定是增大的,而如果某个月的天数多于x,那显然答案取该月的最后x天的和,那么我们就可以枚举每个月作为结尾的情况,答案取最大值
其次,我们可以用b数组记录1 - i月的快乐值之和,用c数组记录1 - i的天数之和,那么当我们枚举某个月为结尾的情况时,我们可以用二分的方法找到这x天开头所在的月份,然后利用b、c数组求出这x天的快乐值

总时间复杂度为O(nlogn)

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 4e5 + 10;
ll a[maxn], b[maxn], c[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	ll n, x;
	cin >> n >> x;
	for (int i = 1; i <= n; ++i) {
		cin >> a[i];
		a[n + i] = a[i];
		b[i] = b[i - 1] + (1 + a[i]) * a[i] / 2;
		c[i] = c[i - 1] + a[i];
	}
	for (int i = 1; i <= n; ++i) {
		b[n + i] = b[n + i - 1] + (1 + a[i]) * a[i] / 2;
		c[n + i] = c[n + i - 1] + a[i];
	}
	ll Max = 0;
	for (int i = 2 * n; i >= n; --i) {
		int l = 1, r = i, res = 1;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (c[i] - c[mid] + a[mid] >= x) {
				l = mid + 1;
				res = max(res, mid);
			}
			else r = mid - 1;
		}
		ll tmp = c[i] - c[res] + a[res] - x;
		ll tmp_sum = b[i] - b[res] + (1 + a[res]) * a[res] / 2;
		Max = max(Max, tmp_sum - (1 + tmp) * tmp / 2);
	}
	cout << Max << '\n';
}

E.Are You Fired?

题目大意

有一个长度为n的数组,给出前[n / 2]([]表示向上取整),后n - [n / 2]恒定为x,问你是否存在这样的k,使得数组中任意长度为k的子序列大于0,若存在输出任意一个k,否则输出-1

解题思路

sum[i]表示1 - i的和
分情况讨论:

  1. 若sum[n] > 0,那么显然k = n满足条件
  2. 若sum[n] <= 0,x >= 0,那么显然不可能有满足条件的k
  3. 否则,我们考虑k的区间一定在(n - [n / 2], n)之间,因为要满足后面的所有x加上前面的和要大于0,那么任意的k子序列一定至少包含一个x,我们首先假设k = n,从头遍历数组,如果sum[l + k - 1] - sum[l - 1] > 0,那么继续,否则我们不断减小子序列(k = k - 1)的长度以不断除去其中的x直至改结果大于0,遍历结束条件为遍历到尾部(输出k)或k <= n - [n / 2](输出-1)

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
ll a[maxn], b[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 1; i <= (n + 1) / 2; ++i) {
		cin >> a[i];
		b[i] = b[i - 1] + a[i];
	}
	ll x;
	cin >> x;
	ll sum = b[(n + 1) / 2] + (n - (n + 1) / 2) * x;
	if (sum > 0) cout << n << '\n';
	else {
		if (x >= 0 || b[(n + 1) / 2] <= 0) cout << "-1\n";
		else {
			for (int i = (n + 1) / 2 + 1; i <= n; ++i) b[i] = b[i - 1] + x;
			ll k = n;
			for (int i = 1; i <= n; ++i) {
				int j = i + k - 1;
				if (j > n) break;
				ll tmp = b[j] - b[i - 1];
				while (tmp <= 0 && k > (n - (n + 1) / 2)) tmp -= x, --k;
				if (k <= (n - (n + 1) / 2)) break;
			}
			if (k <= (n - (n + 1) / 2)) cout << "-1\n";
			else cout << k << '\n';
		}
	}
	return 0;
}

你可能感兴趣的:(codeforces)