【CF】Codeforces Round 698(Div1)_1477C_几何/构造


update 2021-2-4:修复了一个关键实现上的表述问题。

链接

CF1477C

题解

现场得分:过了

我现场的做法

  • 我用了一个调整法,这个方法在随机数据下跑得飞快,最差也只有\(O(n^2)\)
  • 我们考虑当前的点的序列为\(\{A_n\}\)
  • 假设当前我们发现最前面的一个非锐角的地方是\(A_{i-2},A_{i-1},A_i\)(记这个角为\(B_{i-2}\)),则我们如果仅想让这一个点变好,不考虑其他地方,可以直接将序列变成\(A'=...,A_{i-2},A_{i},A_{i-1}\)。根据三角形的性质,我们可以知道这一定是调整成功了的。
  • 但是,有可能导致这里调整好了,\(A_{i-3},A_{i-2},A_i\)这个角不行了。
  • 一个朴素的想法是我们调整完\(B_{i-2}\)之后,往前移一步,看看\(B_{i-3}\)行不行。行的话就继续往后,不行就按上述方法调整\(B_{i-3}\),并再往前看一步。
  • 而这样子调整可以保证调整\(B_{i-3}\)时,不会导致\(B_{i-2}\)不合法(因为变了\(B_{i-3}\)之后,\(B_{i-2}\)成为了\(A_{i},A_{i-2},A_{i-1}\),这显然是合法的)
  • 所以如果\(B_1,...,B_{i-3}\)合法,\(B_{i-2}\)不合法,在调整不超过\(i-2\)次之后就一定会使局面变成\(B_1,...,B_{i-2}\)合法。
  • 所以这个东西的最多调整次数为\(\frac{(n-2)(n-1)}{2}=O(n^2)\)。而事实上调整数字会远远小于他。并且常数极小。

另外的做法

  • 一开始随便选择一个点作为起始。
  • 每次选定距离序列尾部的点最远的点,放入序列尾部。
  • 可以利用大角对大边证明这件事一定是对的。

代码

#include
#define LL long long
#define MAXN 5010
using namespace std;
template void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template void Write(T cn)
{
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
template void WriteL(T cn) {Write(cn); puts(""); }
template void WriteS(T cn) {Write(cn); putchar(' '); }
template void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
struct qwe{
	LL x,y;
	void getit() {Read(x); Read(y); }
	inline friend qwe operator -(qwe cn, qwe cm) {qwe guo; guo.x = cn.x-cm.x; guo.y = cn.y-cm.y; return guo; }
};
LL dianji(qwe cn, qwe cm) {return cn.x*cm.x + cn.y*cm.y; }
qwe a[MAXN+1];
int n;
int xu[MAXN+1];
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Read(n);
	for(int i = 1;i<=n;i++) a[i].getit(), xu[i] = i;
	int cnt = 0;
	for(int i = 3;i<=n;)
	{
		cnt++;
		if(dianji(a[xu[i]]-a[xu[i-1]], a[xu[i-2]]-a[xu[i-1]]) <= 0) {
			swap(xu[i], xu[i-1]); 
			i--; Max(i,3);
		}
		else i++;
		if(cnt >= 100000000) {puts("-1"); return 0; }
//		printf("i = %d cnt = %d %lld\n",i,cnt);
//		for(int j = 1;j<=n;j++) printf("xu[%d] = %d ",j,xu[j]); puts("");
	}
	for(int i = 1;i<=n;i++) WriteS(xu[i]); puts("");
	return 0;
}