Codeforces Round #812 (Div. 2) C ~ D题解

C

题意:

给定一个整数,要求找到一个0 ~ (n - 1) 的排列,使排列的每个数 Ai + i等于平方数并输出找到的序列,如果找不到输出-1.

解题思路: 一眼构造题~~

我们可以得到一个想法 :若Ak + k为平方数 ,且Ak > k 那么我们同时可以构造索引k ~ Ak 这段区域都为平方数 ,举例 第六个放10 第七个放9 第八个放8,直到第十个放6
这样我们就实现构造了一段区间符合条件。
再思考如何完美的实现所有,可以想到一个数离他最近的平方数的距离一定小于他本身,当我们从后往前枚举,索引是固定的,只要找到比当前索引大的最小的一个平方数的差值作为当前位置放置的数,当前放的数到当前索引这一段区间的问题就解决了,而作为每一段区间开始要选择的那个数本身一定比索引小,且一定会用到选择的那个数结尾,之前的所有区间都用不到当前区间所使用的所有数,所有这个构造是可以成立的。

代码实现

本题有多组测试数据,我们需要找到比某个数大的最小的平方数,可以用预处理 + 二分减去枚举的时间,这里使用STL 里的set容器,内含二分查找,非常方便。

预处理
for (int i = 0; i <= 1000 ; i ++)
	{
		if (i * i > 200010)
			break;

		num.insert (i * i);
	}
具体构造
int a[N];
void solve()
{
	int n;
	cin >> n;
	n = n - 1;

	for (int i = n; i >= 0; i --)
	{
		int x = *num.lower_bound (i);// 二分找到最小的平方数
		a[i] = x - i;
		int p = x - i;

		while (p != i and i >= 0)// 循环往前赋值直到碰到区间左端
		{
			i --;
			a[i] = x - i;
		}
	}

	for (int i = 0; i <= n ; i ++)
		cout << a[i] << ' ';

	cout << '\n';
}

D

题意:

给定 2n个选手,每两个选手打淘汰赛,胜者晋级,这是一道交互题, 允许你询问任意两个标号的人的胜场比较,例如a vs b a胜场多则输出1, b胜场多则输出2,持平输出0
询问次数限制为 2 n + 2 3 \frac{2^n +2}{3} 32n+2

解题思路

根据递归的思路,首先想法是将一些最开始胜利的选手根据询问选出来再在这里面询问出当前选手中没有优先淘汰的选手
根据这一思路往下,可以发现,只需要两次询问就可以将四个选手中胜出的选手询问出来
那么这个思路的总询问次数就是 2n * (1 - 1 / 4n ) / (1 - 1 / 4) — 简单等比数列求和

再来说说具体怎么两次询问出4个选手的胜者

如图来看1 2 3 4 四个人的胜者情况
可以知道4个人的胜场分别是 0 0 1 2
2 1 肯定差一场,3 4肯定差一场
那么我们可以先让1 和 3 比 分情况讨论
一、 1 3 中1胜场多
那么1 至少是胜利一场,也就是1 和 2的比赛中1赢了,不用再跟2比较,直接1 4 比较就可以知道胜者
二、1 3 中3胜场多
同一理,只需要再询问一次2 和 3 直接谁胜场多即可
三、1 3 平局
说明 1 3 都是0 只需要再询问2 4 之间胜者即为真胜者
如此这道题我们就A了

代码实现
#include<bits/stdc++.h>
using namespace std;
int Ask (int x, int y)
{
	printf ("? %d %d\n", x, y);
	fflush (stdout);
	int q;
	scanf ("%d", &q);
	return q;
}
int n;
vector<int> a;
void solve()
{
	a.clear();
	scanf ("%d", &n);
	
	for (int i = 1 ; i <= (1 << n) ; i ++)
		a.push_back (i);
	
	while (a.size() > 1)
	{
		if (a.size() == 2)
		{
			int q = Ask (a[0], a[1]);
			a = {q == 1 ? a[0] : a[1]};
			break;
		}
		
		vector<int> b;
		//a数组的个数每回合除以4,我们需要特判个数为2
		for (int i = 0; i < a.size(); i += 4)
		{
			int per1 = a[i], per2 = a[i + 1], per3 = a[i + 2], per4 = a[i + 3];
			int q = Ask (per1, per3);
			
			if (q == 1) // a赢
			{
				int p = Ask (per1, per4);
				
				if (p == 1)
					b.push_back (per1);
				else b.push_back (per4);
			}
			else if (q == 2) // b赢
			{
				int p = Ask (per2, per3);
				
				if (p == 1)
					b.push_back (per2);
				else b.push_back (per3);
			}
			else if (!q)// 平局
			{
				int p = Ask (per2, per4);
				
				if (p == 1)
					b.push_back (per2);
				else b.push_back (per4);
			}
		}
		
		a = b;
	}

	printf ("! %d\n", a[0]);
	fflush (stdout);
}
int main()
{
	int t = 1;
	scanf ("%d", &t);
	
	while (t--)
		solve();
	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值