JZOJ5953. 【NOIP2018模拟11.5A组】生死之境

本文探讨了一个关于幽灵在二维平面上的优美符卡释放算法问题。通过分析幽灵位置与符卡直线的关系,提出了一种优化算法,以减少计算复杂度并找出所有可能的优美符卡释放方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

幽幽子正在练习符卡[反魂蝶八分咲]
冥界可以抽象成一个无限大的二维平面,其中白玉楼在(0,0),冥界上存在着n个幽灵,第i个幽灵位于点(xi,yi)。
一次符卡的释放可以看做一条直线,由于幽幽子身处白玉楼,所以这条直线经过原点。幽幽子想让自己的符卡看起来尽量优美,她定义一次符卡的释放是优美的,当且仅当所有的幽灵在这条直线上的投影是中心对称的。
现在幽幽子想要知道有多少种优美的释放符卡的方式,如果每一种方法都是优美的输出-1。

Data Constraint

对于30%的数据,n<=10
对于60%的数据,n<=200
对于100%的数据,n<=1000,|xi|,|yi|<=10^6,T<=3

题解

首先可以这样,
这n个点的重心,一定是投影的对称重心。
有了这个结论,就可以O(n2)O(n^2)O(n2)枚举一条直线,然后就判断。
这样就是O(n3)O(n^3)O(n3)
还有一个优化,当一条直线的斜率出现次数多与n/2的时候,它才有可能成为答案的直线。
于是复杂度就是O(n2)O(n^2)O(n2)

code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define G getchar
#define db double
using namespace std;
const int N=1003,inf=2147483647;
int n,m,x[N],y[N],T,ans,num,w;
bool bz[N];
db k[N*N],X,Y,s[N],len;
bool pd;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}
void write(int x){if(x>9)write(x/10);putchar(x%10+48);}
int max(int x,int y){return x>y?x:y;}
void check(double k)
{
	for(int i=1;i<=m;i++)
	{
		s[i]=k*y[i]+x[i];i++;
		s[i]=k*y[i]+x[i];i++;
		s[i]=k*y[i]+x[i];
	}
	sort(s+1,s+1+m);
	len=X+Y*k;
	pd=1;
	for(int i=1;i<=m/2;i++)
		if(abs(len-s[i]-s[m-i+1])>0.00000001)
		{
			pd=0;
			break;
		}
	if(pd)ans++;
}

int main()
{
	freopen("life.in","r",stdin);
	freopen("life.out","w",stdout);
	for(read(T);T;T--)
	{
		read(n);ans=0;memset(bz,1,sizeof(bz));X=Y=0;
		for(int i=1;i<=n;i++)read(x[i]),read(y[i]),X=X+x[i],Y=Y+y[i];
		X=X/n*2;Y=Y/n*2;
		for(int i=1;i<=n;i++)
			for(int j=1;j<i;j++)
				if(bz[j] && x[i]+x[j]==X && y[i]+y[j]==Y){bz[i]=bz[j]=0;break;}
		m=w=0;
		for(int i=1;i<=n;i++)if(bz[i])x[++m]=x[i],y[m]=y[i];
		if(m<2){puts("-1");continue;}
		for(int i=1;i<=m;i++)
			for(int j=i;j<=m;j++)
				if(Y-y[i]-y[j]!=0)k[++w]=(x[i]+x[j]-X)/(Y-y[i]-y[j]);
		sort(k+1,k+1+w);k[num=0]=k[1]-1;
		for(int i=1;i<=w;i++)
			if(k[i]!=k[i-1])
			{
				if(num>=m/2)check(k[i-1]);
				num=1;
			}else num++;
		if(num>=m/2)check(k[w]);
		sort(y+1,y+1+m);
		pd=1;
		for(int i=1;i<=m/2;i++)
			if(abs(Y-y[i]-y[m-i+1])>0.00000001)
			{
				pd=0;
				break;
			}
		if(pd)ans++;
		write(ans),putchar('\n');
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值