2019.3.2 B题 排行(交互)

题目大意:

今年是 3345 年,你打算还原千年前这场比赛的名次,但是主办方称由于技术原因排名遗失了,连
用于排名的 a 也丢失了。所幸,在 Byteland 中生活的都是机器人,所以你可以询问这些千年前的参赛
选手。为了谨慎起见,你可以每次询问一个参赛者,某一个参赛者的比赛成绩比它好还是比它差。但是,
机械心理学家告诉你,这些选手不一定愿意回答你的提问。具体地:

  1. 名次小于 1 的选手由于耿耿于怀,如果它应该回答另一个参赛者成绩比它好,它就会选择不回答,
    否则它会如实回答。
  2. 名次为 1 的选手决定闷声大发财,它无论如何都不会回答任何提问。
  3. 名次为 2 的选手只当询问排名为 3 的选手时才回答比排名 3 的好,其他时候都不回答。
  4. 名次为 3 的选手趾高气扬,如果它应该回答另一个参赛者成绩比它好,它就会选择不回答,否则
    它会如实回答。
  5. 名次大于 3 的选手感觉自己水平不行,如果它应该回答另一个参赛者成绩比它差,它就会选择不
    回答,否则它会如实回答。
    你希望通过一些询问还原每个选手的名次,询问数越少你的得分越高,评分细则请看 Notes 一节。

4 ≤ n ≤ 1000,数据保证名次由一个 a < 1 生成,且存在名次为 1, 2, 3 的选手。
本题有若干个测试点,你本题的得分是每个测试点的得分的最小值。
对于某个测试点,若你没有正确还原出每个选手的名次,该测试点得 0 分。
否则设你使用了 x 个询问,设 y = {11500, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 21000},
若 y 中有 a 个大于等于 x 的元素,则该测试点得 10a 分。

题解:

做得太少了还是没有经验。

不难想到将序列分成两个部分,然后分开排序。

排序的话复杂度为 O ( n   l o g   n ) O(n~log~n) O(n log n),使用插入排序可以获得9000次左右的快乐。

那如何分成两个部分,注意回答比自己高的只有 &gt; 3 &gt;3 >3的。

那么不妨扫过去的时候记录最小值,出现过比自己高的就是 &gt; 3 &gt;3 >3的。

这一部分复杂度是 O ( 2 n ) O(2n) O(2n)

然后 &lt; = 3 &lt;=3 <=3的一起排,你会发现排完之后 1 、 2 、 3 1、2、3 123就在序列的末端,但是无法确定顺序,再用至多六次即可判断出位置。

Code:

#include "rank.hpp"
#include<ctime>
#include<algorithm>
#define pp printf
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define fd(i ,x, y) for(int i = x, B = y; i >= B; i --)
using namespace std;
const int N = 1e3 + 5;
int ak[N][N];
int zz(int x, int y) {
	if(ak[x][y] >= 0) return ak[x][y];
	char c = ask(x - 1, y - 1);
	return ak[x][y] = (c == 'n' ? 0 : (c == 'g' ? 1 : 2));
}
int ans[N];

const int inf = 1e9;

int p[N], q[N], bz[N];

int c1(int x, int y) {
	return zz(x, y);
}

int c2(int x, int y) {
	return !zz(x, y);
}

int b[N];
void sort(int *p, int (*cmp)(int x, int y)) {
	fo(i, 1, p[0]) {
		int as = 0;
		for(int l = 1, r = i - 1; l <= r; ) {
			int mi = l + r >> 1;
			if(cmp(b[mi], p[i])) as = mi, l = mi + 1; else r = mi - 1;
		}
		fd(j, i, as + 2) b[j] = b[j - 1];
		b[as + 1] = p[i];
	}
	fo(i, 1, p[0]) p[i] = b[i];
}

vector<int> work(int n)
{
	vector<int> V;
	fo(i, 1, n) fo(j, 1, n) ak[i][j] = -1;
	fo(i, 1, n) ans[i] = inf;
	
	int x = 1;
	fo(i, 2, n) {
		int u = zz(x, i), v = zz(i, x);
		if(u == 1) bz[x] = 1;
		if(v == 1) bz[i] = 1;
		if(u == 1 || v == 2) x = i;
	}
	fo(i, 1, n) if(bz[i]) q[++ q[0]] = i; else p[++ p[0]] = i;
	sort(p, c1); sort(q, c2);
	int u, v;
	fo(i, p[0] - 2, p[0]) fo(j, p[0] - 2, p[0]) if(i != j)
		if(c1(p[i], p[j])) { u = p[i], v = p[j]; fo(k, p[0] - 2, p[0]) if(k != i && k != j) x = p[k]; break;}
	ans[x] = 1; ans[u] = 2; ans[v] = 3;
	fd(i, p[0] - 3, 1) ans[p[i]] = 3 - (p[0] - i);
	fo(i, 1, q[0]) ans[q[i]] = 3 + i;
	fo(i, 1, n) V.push_back(ans[i]);
	return V;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值