【做题笔记】CF1254C Point Ordering

感谢小笼包凸包之神 djwj223 提供的思路。

Problem

CF1254C Point Ordering

题目大意:

交互题,有一个由 \(n\) 个顶点组成的没有三点共线的凸包,每次可以有两种询问:

  • 第一种询问三个点,返回这三个点组成的三角形的面积的 \(2\) 倍。
  • 第二种询问三个点 \(A_i,A_j,A_k\),返回 \(\overrightarrow{A_iA_j} \times \overrightarrow{A_iA_k}\) 的正负。

要求在 \(3n\) 次询问后从 \(1\) 开始逆时针输出点的编号。如:

应该输出 0 1 3 4 2 6 5

Solution

考虑第二种询问相当于询问 \(A_k\)\(A_iA_j\) 的左侧(正)还是右侧(负)。

然后考虑钦定两个点,发现其他的点与这两个点的询问可以求出他们构成的三角形的高(以钦定的两个点连成的线段为底)。

于是就有这样一个想法:
任意找两个点,然后用 \(n-2\) 次询问得到其他点在左侧还是右侧。再使用 \(n-2\) 次询问得到其他点到直线的距离的相对大小。容易发现直线切割后的两半部分都依然是凸包,所以点到直线的相对大小必定是先增大再减小。

接下来的问题就是对于每一部分确定哪些在左侧,哪些在右侧。
一个比较容易想到的做法是找到距离最大的点,然后用操作 \(2\) 区分点在左侧还是右侧。然后对于右侧从小到大排序,左侧从大到小排序。

由于两部分都会取出一个顶点,再去掉最初钦定的两个点,要对其余的每个点进行询问,所以需要 \(n-4\) 次询问。

但是最高点可能有两个,不过手玩一下就会发现没什么问题。

总询问次数 \(n-2+n-2+n-4=3n-8\),但是当最初选择的两个点相邻的时候会变成 \(n-2+n-2+n-3=3n-7\)

还有不懂的可以看代码:

Code

//Think twice,code once.
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,p;
long long mx;
vector<pair<long long,int> >r,l,a1,a2;
vector<int>ans;
int main()
{//钦定最初的两个点1 2
	scanf("%d",&n);
	for(int i=3;i<=n;i++)
	{
		printf("2 1 2 %d\n",i);fflush(stdout);
		int x;scanf("%d",&x);
		if(x==-1) r.emplace_back(0,i);
		else l.emplace_back(0,i);
	}//区分左右侧
	ans.push_back(1);
    //先处理右侧
	for(int i=0;i<(int)r.size();i++)
	{
		printf("1 1 2 %d\n",r[i].second);fflush(stdout);//询问点到直线的距离
		scanf("%lld",&r[i].first);
		if(r[i].first>mx) mx=r[i].first,p=r[i].second;//记录最高点
	}
	for(auto i:r)
	{
		if(i.second==p) continue;
		printf("2 1 %d %d\n",p,i.second);fflush(stdout);//判断在最高点左侧还是右侧
		int x;scanf("%d",&x);
		if(x==-1) a1.push_back(i);
		else a2.push_back(i);
	}
	sort(a1.begin(),a1.end());sort(a2.begin(),a2.end());reverse(a2.begin(),a2.end());//右侧从小到大,左侧从大到小
	for(auto i:a1) ans.push_back(i.second);
	if(p) ans.push_back(p);
	for(auto i:a2) ans.push_back(i.second);
	ans.push_back(2);
	p=mx=0;a1.clear();a2.clear();
    //处理左侧
	for(int i=0;i<(int)l.size();i++)
	{
		printf("1 1 2 %d\n",l[i].second);fflush(stdout);//同上
		scanf("%lld",&l[i].first);
		if(l[i].first>mx) mx=l[i].first,p=l[i].second;//同上
	}
	for(auto i:l)
	{
		if(i.second==p) continue;
		printf("2 1 %d %d\n",p,i.second);fflush(stdout);//同上
		int x;scanf("%d",&x);
		if(x==-1) a1.push_back(i);
		else a2.push_back(i);
	}
	sort(a1.begin(),a1.end());sort(a2.begin(),a2.end());reverse(a2.begin(),a2.end());//同上
	for(auto i:a1) ans.push_back(i.second);
	if(p) ans.push_back(p);
	for(auto i:a2) ans.push_back(i.second);
	printf("0 ");
	for(auto i:ans) printf("%d ",i);
	puts("");
	return 0;
}
posted @ 2022-09-23 18:08  Mine_King  阅读(35)  评论(0编辑  收藏  举报