「ROI 2017 Day 1」虎 (计算几何)

「ROI 2017 Day 1」虎 (计算几何)

题意:(交互题)

已知\(n\)个点,\(m\)次询问,每次询问交互器随机生成一个位置的关键点,要求在\(k\)次查询中给出一个合法解

查询:一个凸包,返回关键点是否在凸包中

解:一个凸包,包含关键点,且不包含其它点,保证有界

对关键点的包含是包括了边界线的,其它点的包含不包括边界线,凸包均要按照顺时针给出

Solution 1 (未完全实现)

先将给定点分层转化为若干凸包,容易通过二分得到关键点所在的层

如果关键点以内不再有凸包,显然得到一个解

否则,一个合法的解一定是在两层凸包中某一层选取两个点,另一层选取一个点得到的三角形

先考虑内层选两个点的情况,令二分边界为内层凸包上点的编号,\(l,r\)

考虑实现一个操作,对于\(l,r\),找到其连接的直线在顺时针方向上在外层凸包上切到的段

由此得到一个凸包进行查询,即可进行二分,最终\(l+1=r\)时,再进行一次上述操作得到一组解(不一定合法)

如果不合法,同理再在外层上二分一次

最终写道第一种情况弃掉了。。。

\[\ \]

\[\ \]

Solution2 随机分裂

良好的随机分裂可以跑到max query times<=33的好成绩

考虑一个非常简单的剖开\(n\)个点的方法:

1.找到这些点的凸包,从点集中删掉

2.从剩余点中选择一个作为中心,分别与凸包上的点连边,将平面分开

3.确定每个三角形中包含的点,加上三个顶点,继续进行剖分

最终当凸包以外不再有点时,结束

\[\ \]

查询也是比较显然的:

对于当前凸包及其中心,二分找到关键点对应的位置,然后继续进行,直到不存在中心

二分方法:

凸包构成一圈,取一个点为\(l\),顺时针180以内的范围最大角度的点为\(r\),每次将中心和\(l,mid\)这连续一段查询判断是否包含

这样存在的问题是:可能不在\(l\)的180范围内,需要在开始二分前判断一下

十分朴素的实现也可以获得90分的好成绩

优化:

每次选取中心时,多随机几次,估价找到一个最优剖分即可

Loj Submission -包含大量调试语句和内嵌的交互部分

Loj Submission

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

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T& a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T& a,T b){ ((a<b)&&(a=b)); }

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

bool Mbe;
const int N=5010;

int n,q;

// 计算几何部分
namespace Geometry{
struct Node{
	ll x,y;
	Node(){}
	Node(ll x,ll y):x(x),y(y){}
	Node operator - (const Node __) const { return Node(x-__.x,y-__.y); }
	Node operator + (const Node __) const { return Node(x+__.x,y+__.y); }
	ll operator ^ (const Node __) const { return 1ll*x*__.y-1ll*y*__.x; }
	bool operator == (const Node __) const { return x==__.x && y==__.y; }
	void Read(){ x=rd(),y=rd(); }
} A[N],QuePoint;
typedef vector <int> V;
// check close wise
int chk(int i,int j,int k,int bound=0){ return ((A[k]-A[i])^(A[j]-A[i]))>=bound; }
int chk(Node i,int j,int k,int bound=0){ return ((A[k]-i)^(A[j]-i))>=bound; }
// this is clockwise
int CheckConvex(const V &P){
	int n=P.size();
	rep(i,0,n-1) {
		int j=(i+1)%n,k=(j+1)%n;
		if(!chk(P[j],P[k],P[i])) return 0;
	}
	return 1;
}
// this is clock-wise
int CheckIn(const V&P,Node X,int bound=0){
	rep(i,0,P.size()-1) {
		int j=(i+1)%P.size();
		if(chk(X,P[i],P[j],bound)) continue;
		return 0;
	}
	return 1;
}
int Check(V P){
	if(P.size()<=2) return 0;
	printf("? %llu ",P.size());
	for(int i:P) printf("%d ",i); 
	puts("");
	fflush(stdout);
	static char s[5]; scanf("%s",s);
	return *s=='Y';
}
void Output(V P){
	printf("! %llu ",P.size());
	for(int i:P) printf("%d ",i);
	puts("");
	fflush(stdout);
}
V Convex(V P){
	if(P.size()<=2) return P;
	V Ans;
	int n=P.size();
	static int I[N],mk[N],S[N],T;
	rep(i,0,n-1) mk[I[P[i]]=i]=0;
	sort(P.begin(),P.end(),[&](int x,int y){ return A[x].x<A[y].x || (A[x].x==A[y].x && A[x].y>A[y].y); });
	T=0;
	rep(i,0,n-1) {
		while(T>1 && ((A[P[i]]-A[S[T]])^(A[S[T-1]]-A[S[T]]))>0 ) T--;
		S[++T]=P[i];
	}
	rep(i,1,T) Ans.pb(S[i]),mk[I[S[i]]]=1;
	sort(P.begin(),P.end(),[&](int x,int y){ return A[x].x<A[y].x || (A[x].x==A[y].x && A[x].y<A[y].y); });
	T=0;
	rep(i,0,n-1) {
		while(T>1 && ((A[P[i]]-A[S[T]])^(A[S[T-1]]-A[S[T]]))<0 ) T--;
		S[++T]=P[i];
	}
	drep(i,T,1) if(!mk[I[S[i]]]) Ans.pb(S[i]);
	return Ans;
}
}
using namespace Geometry;


const int M=2e5+10;

V C[M],S[M];
int K[M],D[M],E[M],rt,m,mk[M];
// 剖分
// C凸包,S子节点
// K中心,D凸包上0号点对应的180范围内的最大点,E凸包上D号点对应180范围内的最大点
// m个数
void Build(int &u,V P) {
	sort(P.begin(),P.end());
	int n=P.size();
	C[u=++m]=Convex(P);
	rep(i,0,n-1) mk[i]=0;
	if(C[u].size()==P.size()) return;
	for(int i:C[u]) mk[lower_bound(P.begin(),P.end(),i)-P.begin()]=1;
	V T; rep(i,0,n-1) if(!mk[i]) T.pb(P[i]);
	n=C[u].size();
    // 获取剖分结果
	auto Get=[&]() {
		if(rand()&1) K[u]=T[(T.size()/(rand()%3+2)+rand()%8)%T.size()];
		else K[u]=T[rand()%T.size()];
		V P=T; P.erase(lower_bound(P.begin(),P.end(),K[u]));
		D[u]=E[u]=0;
		while(D[u]<n-1 && chk(K[u],C[u][0],C[u][D[u]+1])) D[u]++;
		E[u]=D[u];
		while(E[u]<n-1 && chk(K[u],C[u][D[u]],C[u][E[u]+1])) E[u]++;
		vector <V> ST(n);
		for(int x:P) {
			rep(i,0,n-1) {
				if(chk(K[u],C[u][i],x) && chk(K[u],x,C[u][(i+1)%n])) {
					ST[i].pb(x);
					break;
				}
			}
		}
		rep(i,0,n-1) ST[i].pb(K[u]),ST[i].pb(C[u][i]),ST[i].pb(C[u][(i+1)%n]);
		return ST;
	};
	vector <V> st;
	int mi=1e9;
	rep(kase,1,5) {
		int d=D[u],e=E[u],k=K[u];
		auto ST=Get();
		int now=0;
		for(V j:ST) cmax(now,(int)j.size());
		if(now>mi){ D[u]=d,E[u]=e,K[u]=k; continue; }
		st=ST,mi=now;
	}
	rep(i,0,n-1) {
		int x; Build(x,st[i]);
		S[u].pb(x);
	}
}

void Init() {
	V T(n);
	rep(i,0,n-1) T[i]=i+1;
	Build(rt,T);
}

void Find(int u=rt){
	if(!S[u].size()) return Output(C[u]);
	int n=C[u].size();
	int l,r;
	auto Get=[&](int l,int r) {
		V T; T.pb(K[u]);
		l%=n,r%=n;
		for(int i=l;;i=(i+1)%n) {
			T.pb(C[u][i]);
			if(i==r) break;
		}
		return Check(T);
	};
	if(Get(0,D[u])) l=0,r=D[u];
	else if(Get(D[u],E[u])) l=D[u],r=E[u];
	else l=E[u],r=n;
	while(l+1<r) {
		int mid=(l+r)>>1;
		if(Get(l,mid)) r=mid;
		else l=mid;
	}
	Find(S[u][l]);
}

int main(){ 
	n=rd();
	rep(i,1,n) A[i].Read();
	Init();
	q=rd();
	rep(_,1,q) Find();
}

posted @ 2021-02-01 20:59  chasedeath  阅读(290)  评论(0编辑  收藏  举报