Codeforces Round #266 (Div. 2) B & C

哎~又被虐了

还是好好总结下下吧。


题目连接:http://codeforces.com/contest/466/problem/B

B. Wonder Room
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

The start of the new academic year brought about the problem of accommodation students into dormitories. One of such dormitories has a a × b square meter wonder room. The caretaker wants to accommodate exactly n students there. But the law says that there must be at least 6 square meters per student in a room (that is, the room for n students must have the area of at least 6n square meters). The caretaker can enlarge any (possibly both) side of the room by an arbitrary positive integer of meters. Help him change the room so as alln students could live in it and the total area of the room was as small as possible.

Input

The first line contains three space-separated integers na and b (1 ≤ n, a, b ≤ 109) — the number of students and the sizes of the room.

Output

Print three integers sa1 and b1 (a ≤ a1b ≤ b1) — the final area of the room and its sizes. If there are multiple optimal solutions, print any of them.

Sample test(s)
input
3 3 5
output
18
3 6
input
2 4 4
output
16
4 4

题意:有n个学生要住在a*b这么大面积的房间里,要求每个学生至少有6平方米的居住面积,也就是要找到这样的a1和b1,使得6*n <= min{a1*b1},其中a<=a1 && b<=b1。注意!1 <= n, a, b <= 1e9

思路:Let’s assume that a ≤ b.

First of all, let’s consider the situation when we can already accommodate all the students. If n ≤ a·b then answer is a·b a b.

Otherwise, we have to increase one of the walls(maybe, both). Let’s do it in the following way: iterate the size of the smallest wall newa ( ), after that we can calculate the size of another wall as 
For all this newa and newb if b ≤ newb we choose such a pair that has the smallest area of a room.

Obviously to undestrand, that there is no point to consider  because we can decrease it and receive room of smaller area when we know that .

Complexity

最开始考虑的竟然是二分查找!被自己的机智吓到了,可惜,准确度不行啊,还是得O(sqrt(n))的复杂度。

代码:

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
using namespace std;

void swap(__int64 *a, __int64 *b)
{
	__int64 tmp;
	tmp = *a;
	*a = *b;
	*b = tmp;
}

int main()
{
	__int64 n, a, b;
	cin >> n >> a >> b;
	if (6*n <= a*b)
	{
		cout << a*b << endl;
		cout << a << " " << b << endl;
	}
	else
	{
		bool flag = false;
		if (a > b)
		{
			swap(a, b);
			flag = true;
		}
		__int64 maxsq = 1e18;
		__int64 newa, newb;

		// 注意要向上取整!
		for (__int64 a1 = a; a1 <= ceil(sqrt(6.0*n)); a1++)
		{
			__int64 b1 = ceil(6.0*n/a1);
			// b1 <= 1e9 精度很重要
			if (b1 >= b && b1 <= 1e9 && a1*b1 <= maxsq)
			{
				newa = a1;
				newb = b1;
				maxsq = a1*b1;
			}
		}

		// 把交换了的长和宽再置换回来
		if (flag)
			swap(newa, newb);
		cout << maxsq << endl;
		cout << newa << " " << newb << endl;
	}

	return 0;
}


题目连接:http://codeforces.com/contest/466/problem/C

C. Number of Ways
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You've got array a[1], a[2], ..., a[n], consisting of n integers. Count the number of ways to split all the elements of the array into three contiguous parts so that the sum of elements in each part is the same.

More formally, you need to find the number of such pairs of indices i, j (2 ≤ i ≤ j ≤ n - 1), that .

Input

The first line contains integer n (1 ≤ n ≤ 5·105), showing how many numbers are in the array. The second line contains n integers a[1],a[2], ..., a[n] (|a[i]| ≤  109) — the elements of array a.

Output

Print a single integer — the number of ways to split the array into three parts with the same sum.

Sample test(s)
input
5
1 2 3 0 3
output
2
input
4
0 1 -1 0
output
1
input
2
4 1
output
0

题意:有一个长度为n的数组,把数组划分为连续的三个部分,要求三个部分之和相等,找出所有满足这样条件的划分的个数。

思路:First of all, notice that if sum of all elements is equal S then sum of each of three parts is equal .

Therefore, if S is not divided by 3 — then answer is 0.
Otherwise, let’s iterate the end of first part i (1 ≤ i ≤ n - 2) and if sum of 1..i elements is equal  then it means that we have to add to the answer the amount of such j (i + 1 < j) that the sum of elements from j-th to n-tn also equals .

Let’s create an array cnt[], where cnt[i] equals 1, if the sum of elements from i-th to n-th equals  and 0 — otherwise. Now, to calculate the answer we have to find the sum cnt[j] + cnt[j+1] + ... + cnt[n] faster then O(n). There are a lot of required ways to do this, but the easiest one is to create a new additional array sums[] where in j-th element will becnt[j] + cnt[j+1] + ... + cnt[n]. It is easy to calculate in such way: sums[n] = cnt[n],sums[i] = sums[i+1] + cnt[i] (i < n).

Thus, we receive very simple solution: for each prefix of initial array 1..i with the sum that equals  we need to add to the answersums[i+2].

ComplexityO(n)

之前看了半天没看明白,还是好好解释下吧。

首先,假设所有元素之和为S,那么三个部分每部分之和都是S/3.

因此,凡是不能被3整除或者元素个数少于3个的输出结果必然为0.

在以上情况之外,我们的做法是首先遍历这n个元素,找到前i个元素(0,1, .. , i-1)之和为S/3;在满足了前i个元素之和为S/3的情况下,我们从第i+2个元素开始查找(因为划分为三部分,至少有一个元素作为第二部分的),使得从第j(i+1 <j<=n)个元素到第n个元素之和也为S/3;如此一来,中间那部分的元素之和也必然为S/3,也就找到了这样一个满足条件的划分。

前i个元素之和为S/3很好解决,算法的关键转化为找到从第j个元素到第n个元素之和也为S/3。创建数组cnt[],若j-th到n-th之和为S/3,则令cnt[j]=1,否则cnt[j]=0。那么sums[j] = cnt[j]+cnt[j+1]+..+cnt[n]恰好为j-th到n-th之和为S/3的元素个数。关于sum[]的快速计算方法,令sums[n]=cnt[n],sums[j] = sums[j+1]+cnt[i]。

最后,满足条件的划分为 cnt[i+2]+..+cnt[n] = sums[i+2].

代码:

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#define MAX 500010
using namespace std;

__int64 a[MAX];
__int64 cnt[MAX], sums[MAX];

int main()
{
	__int64 n;
	cin >> n;
	__int64 sum = 0;
	for (__int64 i = 0; i < n; i++)
	{
		cin >> a[i];
		sum += a[i];
	}

	if (n < 3 || sum % 3)
	{
		cout << "0" << endl;
		return 0;
	}

	// 初始化cnt[]
	__int64 revsum = 0;
	for (__int64 i = n-1; i >= 0; i--)
	{
		revsum += a[i];
		if (revsum == sum/3)
			cnt[i] = 1;
	}

	// dp计算sums[]
	sums[n-1] = cnt[n-1];
	for (__int64 i = n-2; i >= 0; i--)
		sums[i] = sums[i+1]+cnt[i];

	__int64 s = 0;
	__int64 ans = 0;
	// 依算法特性计算结果
	for (__int64 i = 0; i < n-2; i++)
	{
		s += a[i];
		if (s == sum/3)
			ans += sums[i+2];
	}

	cout << ans << endl;

	return 0;
}

只有更加地努力,才不要一直菜下去!

我知道会很艰难,但是不痛苦就不会有成长。把人生的一切都当做经历吧,只有经历得越多,心灵才会更加丰富,去体验吧、经历吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章