Codeforces Round #703 (Div. 2)

这篇博客探讨了三个编程问题,包括如何通过操作使数组严格递增,寻找平面上点集到最短距离之和的点的数量,以及在限制查询次数下找出数组的最大值下标。每个问题都提供了思路和解决方案,涉及数组操作、二分查找和交互式问题的策略。
摘要由CSDN通过智能技术生成

继续上次开的坑
A题传送门
You have n stacks of blocks. The i-th stack contains hi blocks and it’s height is the number of blocks in it. In one move you can take a block from the i-th stack (if there is at least one block) and put it to the i+1-th stack. Can you make the sequence of heights strictly increasing?

Note that the number of stacks always remains n: stacks don’t disappear when they have 0 blocks.

Input
First line contains a single integer t (1≤t≤104) — the number of test cases.

The first line of each test case contains a single integer n (1≤n≤100). The second line of each test case contains n integers hi (0≤hi≤109) — starting heights of the stacks.

It’s guaranteed that the sum of all n does not exceed 104.

Output
For each test case output YES if you can make the sequence of heights strictly increasing and NO otherwise.

You may print each letter in any case (for example, YES, Yes, yes, yEs will all be recognized as positive answer).

Example
inputCopy
6
2
1 2
2
1 0
3
4 4 4
2
0 0
3
0 1 0
4
1000000000 1000000000 1000000000 1000000000
outputCopy
YES
YES
YES
NO
NO
YES
Note
In the first test case there is no need to make any moves, the sequence of heights is already increasing.

In the second test case we need to move one block from the first stack to the second. Then the heights become 0 1.

In the third test case we could move one block from the first stack to the second and then from the second to the third, which would make the heights 3 4 5.

In the fourth test case we can’t make a move, but the sequence is not increasing, so the answer is NO.

In the fifth test case we can only make one move (from the second to the third stack), which would make the heights 0 0 1. Both 0 1 0 and 0 0 1 are not increasing sequences, so the answer is NO.

题目大意:
给你一个n个元素的数组,你可以执行任意次操作(把前一个元素减一,后一个元素加一),问你能不能用出神奇的操作让数组变得严格递增。
解题思路;
考虑朴素的算法,我们可以按照自己的直觉去判断每一个地方需不需要做操作,但是我同时又发现,当前这个元素是否要进行操作我还得考虑当前元素的上一个元素的大小,并且,还得不断的往回去判断,也就是如果我按照朴素的方法去思考的话,时间复杂度将会是O(n^2),这个是我们所不能满意的,所以换一个方式思考,这道题也没有特别好的解法呢?

这道题的最优情况肯定是,数组元素是0 1 2 3 4 5 … n排列下去,换句话说,这样的排列所需的元素总和最小,我们只要不管三七二十一,把整个数组变成最优情况,然后把多余的数值全部扔到最后的n上去,具体操作看代码

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define pb emplace_back
#define PII pair<int,int>
#define ll long long
#define mp make_pair
const int maxn = 2e6 + 10;

void solve() {
	ll n, ans = 0,cnt,flag = 1;
	ll a[120] = {};
	cin >> n;
	cin >> a[0];
	ans = a[0];
	for (int i = 1; i < n; i++)
	{
		cin >> a[i];
		ans += a[i] - i;
		if (ans < 0)flag = 0;
		a[i] = i;
	}
	puts(flag ? "YES" : "NO");
}
int main()
{
	cin.sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}

B题传送门
B. Eastern Exhibition
You and your friends live in houses. Each house is located on a 2D plane, in a point with integer coordinates. There might be different houses located in the same point. The mayor of the city is asking you for places for the building of the Eastern exhibition. You have to find the number of places (points with integer coordinates), so that the summary distance from all the houses to the exhibition is minimal. The exhibition can be built in the same point as some house. The distance between two points and is , where is the absolute value of .

Input
First line contains a single integer — the number of test cases.

The first line of each test case contains a single integer . Next lines describe the positions of the houses .

It’s guaranteed that the sum of all does not exceed .

Output
For each test case output a single integer - the number of different positions for the exhibition. The exhibition can be built in the same point as some house.

Example
inputCopy
6
3
0 0
2 0
1 2
4
1 0
0 2
2 3
3 1
4
0 0
0 1
1 0
1 1
2
0 0
1 1
2
0 0
2 0
2
0 0
0 0
outputCopy
1
4
4
4
3
1
Note
Here are the images for the example test cases. Blue dots stand for the houses, green — possible positions for the exhibition.

题目大意:
给你平面上n个点,请你找出到这些点距离之和的最短的所有点。并且输出它的数量。
解题思路:
对于所有的题目,我们都可以将其转化成更为简单易懂的题来思考, 比如我们可以把这题的二维转化成一维,因为可以发现,(1,1)(0,0)两个点的结果是和(1,0)(0,1)没有区别的,仔细思考一下,会发现,你完全可以分开考虑两个坐标轴,对x的数组求出x的正中间是啥,比如有奇数个点 x坐标是1 2 3,那么题目求的点的x坐标一定等于2,否则一定不会满足距离之和最短,比如偶数个点,x坐标分别是1 2 3 4,那么最后所求的点的x坐标一定是2或者3,所以我们可以对x坐标数组和y坐标数组进行排序,然后用这个表达式和求出对于待求点x坐标的范围的宽度和y坐标范围的宽度,然后相乘,就是结果啦。
代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define pb emplace_back
#define PII pair<int,int>
#define ll long long
#define mp make_pair
const int maxn = 2e6 + 10;
ll solve(vector<int>a)
{
	sort(a.begin(), a.end());
	return 1 + a[a.size() / 2] - a[(a.size() - 1) / 2];
}
void solve() {
	int n; cin >> n;
	vector<int>a(n),b(n);
	for (int i = 0; i < n; i++)	cin >> a[i] >> b[i];
	cout << solve(a) * solve(b) << '\n';
}
int main()
{
	cin.sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}

	return 0;
}

C题传送门
这题的两个版本合起来讲了
所以下面放的是hard version

C2. Guessing the Greatest (hard version)

The only difference between the easy and the hard version is the limit to the number of queries.

This is an interactive problem.

There is an array of different numbers. In one query you can ask the position of the second maximum element in a subsegment . Find the position of the maximum element in the array in no more than 20 queries.

A subsegment is all the elements . After asking this subsegment you will be given the position of the second maximum from this subsegment in the whole array.

Input
The first line contains a single integer — the number of elements in the array.

Interaction
You can ask queries by printing "? " . The answer is the index of the second maximum of all elements . Array is fixed beforehand and can’t be changed in time of interaction.

You can output the answer by printing "! ", where is the index of the maximum element in the array.

You can ask no more than 20 queries. Printing the answer doesn’t count as a query.

After printing a query do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

fflush(stdout) or cout.flush() in C++;
System.out.flush() in Java;
flush(output) in Pascal;
stdout.flush() in Python;
see documentation for other languages
Hacks

To make a hack, use the following test format.

In the first line output a single integer . In the second line output a permutation of integers to . The position of in the permutation is the position of the maximum

Example
inputCopy
5

3

4

outputCopy
? 1 5

? 4 5

! 1
Note
In the sample suppose is . So after asking the subsegment is second to max value, and it’s position is . After asking the subsegment is second to max value and it’s position in the whole array is .

Note that there are other arrays that would produce the same interaction, and the answer for them might be different. Example output is given in purpose of understanding the interaction.

题目大意:这题是一个交互题,给你一个有n个元素的数组,你需要用20次以内的询问(可以询问l到r区间次大值的下标)来找出最大值的下标(从1开始)。
解题思路:
先根据询问次数20次可以得知这题要用二分查找的思想,因为这个明显是logn级别的次数。
然后给出标准思路

123456
512346

假设我们有这样一个数组待询问,我们可以知道,最大值的下标是 6,次大值下标是1,
我们容易知道最后一次询问的区间边界一定是和这两个值有关系,因为,我们的询问可以判断最大值是不是在询问的区间内,我们首先询问整个数组得知次大值的下标,然后以次大值的下标为断点,不断的往周围试探最大值是否被包含在其中,那么如何试探呢,回想刚刚所涉及的二分算法,就水落石出了。
一般情况下,第二步,我们可以把二分的区间(注意不是查询的区间)一边设置成次大值下标,另一边设置成数组末端,那么我们就可以判断出数组的右边是否有最大值,如果有最大值,那么我们就可以把 l 往右移动,但是实际上查询的区间的左端点依旧是次大值的下标,直到我们发现二分的区间长度不大于2了(当里面只剩下两个元素的时候,其中一定有一个元素被判断过了,比如当你从 l = 4 r = 6变成 l = 5 r = 6的时候,我们就可以直接判断出最大值处在5还是6了,因为只有check(5)返回假才会把执行l = mid,也就是说明,答案就是6),我们就知道答案在哪里了。
代码如下(题解标准代码,并未改动):

#include <bits/stdc++.h>
using namespace std;

int ask(int l, int r) {
	if (l >= r) return -1;
	cout << "? " << l + 1 << ' ' << r + 1 << endl;
	int ans;
	cin >> ans;
	return ans - 1;
}

int main() {
	int n;
	cin >> n;
	int smax = ask(0, n - 1);
	if (smax == 0 || ask(0, smax) != smax) {
		int l = smax, r = n - 1;
		while (r - l > 1) {
			int m = (l + r) / 2;
			printf("l = %d,r = %d\n", l+1, r+1);
			if (ask(smax, m) == smax) {
				r = m;
			}
			else {
				l = m;
			}
		}
		printf("l = %d,r = %d\n", l+1, r+1);
		cout << "! " << r + 1 << endl;
	}
	else {
		int l = 0, r = smax;
		while (r - l > 1) {
			int m = (l + r) / 2;
			printf("l = %d,r = %d\n", l+1, r+1);
			if (ask(m, smax) == smax) {
				l = m;
			}
			else {
				r = m;
			}
		}
		printf("l = %d,r = %d\n", l+1, r+1);
		cout << "! " << l + 1 << endl;
	}
	return 0;
}

D题传送门
D. Max Median

You are a given an array a of length n. Find a subarray a[l…r] with length at least k with the largest median.

A median in an array of length n is an element which occupies position number ⌊n+12⌋ after we sort the elements in non-decreasing order. For example: median([1,2,3,4])=2, median([3,2,1])=2, median([2,1,2,1])=1.

Subarray a[l…r] is a contiguous part of the array a, i. e. the array al,al+1,…,ar for some 1≤l≤r≤n, its length is r−l+1.

Input
The first line contains two integers n and k (1≤k≤n≤2⋅105).

The second line contains n integers a1,a2,…,an (1≤ai≤n).

Output
Output one integer m — the maximum median you can get.

Examples
inputCopy
5 3
1 2 3 2 1
outputCopy
2
inputCopy
4 2
1 2 3 4
outputCopy
3
Note
In the first example all the possible subarrays are [1…3], [1…4], [1…5], [2…4], [2…5] and [3…5] and the median for all of them is 2, so the maximum possible median is 2 too.

In the second example median([3…4])=3.

题目大意:
给定一个含有n个元素的数组,再给定一个k,至少需要连续取k个数字,询问取的数字的中位数最大值。
解题思路:
对于这个摸不着头绪的问题,就可以用玄学二分来思考,在第一篇博客里我有详细讲过这个思路,我们可以把求值的问题转化成枚举答案的问题,因为我们知道这个问题有单调性而且边界已知。
那么我们又需要思考什么呢?既然谈到二分我们就需要思考check 函数怎么写,很明显,我们枚举的这个i,假定它是最大的中位数那么我们一定可以在原数组当中找到长度至少为k的子数组,里面有最多一半的元素大于等于这次枚举的i。
根据以上的分析,我们可以知道check函数的具体实现了。

bool check(int m)
{
	vector<int> b(n);//用1和-1说明原数组的数字是大于等于枚举的m还是小于枚举的m
	for (int i = 0; i < n; ++i)//修改新数组
		if (a[i] > m)b[i] = 1;
		else b[i] = -1;
	for (int i = 1; i < n; ++i) b[i] += b[i - 1];//求前缀和
	int mx = b[k - 1], mn = 0;//找最值
	for (int i = k; i < n; ++i) {
		mn = min(mn, b[i - k]);//按顺序从0~n-k-1中寻找前缀和最小值
		mx = max(mx, b[i] - mn);//同时来利用对应的最小值求出前缀和最大值
	}
	return mx > 0;//判断前缀和最大值是不是大于0
}

代码如下:(基本根据cf官方题解改写,有兴趣的童鞋可以自行去官网查看)


#include <bits/stdc++.h>
using namespace std;
int n, k;
vector<int> a(200000);
bool check(int m)
{
	vector<int> b(n);
	for (int i = 0; i < n; ++i)
		if (a[i] > m)b[i] = 1;
		else b[i] = -1;
	for (int i = 1; i < n; ++i) b[i] += b[i - 1];
	int mx = b[k - 1], mn = 0;
	for (int i = k; i < n; ++i) {
		mn = min(mn, b[i - k]);
		mx = max(mx, b[i] - mn);
	}
	return mx > 0;
}
int main() {
	cin >> n >> k;
	for (int i = 0; i < n; i++)	cin >> a[i];
	int l = 1, r = n;
	while (r>l) {
		int m = (l + r) / 2;
		if (check(m)) l = m + 1;
		else r = m;
	}
	cout << l << '\n';
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值