Codeforces 975E - Hag‘s Khashba (计算几何 + 多边形重心性质 + 旋转)

在这里插入图片描述
在这里插入图片描述
题意:看上去很复杂,就是他给定一个严格的凸多边形,然后hag有两个钉子,最开始他会把这个两个钉子钉在第一个顶点和第二个顶点处,然后这个多边形就被固定了。他有两种操作:
1 f t:hag会把第f个钉子给拆了,然后由于现在只有一个钉子钉在某个顶点上,那么这个多边形框就会在重力的作用下,摆动起来,最后停在某个位置时,这时再把拆下来的钉子放在t点。
2 v:输出在多边形上此时第v个坐标的是多少。

思路:首先我们需要知道,在重力作用下稳定的时候,重心是会和那个被固定的顶点在一条垂线上,也就是说他们两个这时候横坐标会相等。
每次拆钉子后,重新改变钉子的位置,肯定是会超时的。所以我们考虑维护一下初始状态时各个顶点与重心位置的相对位置,也就是个角度。
这样我每次需要改变的点的左边其实就是当前两个被钉子钉住的点即可,因为剩下的点都可以通过他们与钉住的点和重心的相对位置关系来确定实时位置。
具体需要维护的信息:
一开始先把所有点与初始重心的角度关系找出来。 然后每次拔钉子更新时,重心会跑到那个没被拔的钉子的正下方,此时可以通过这个点与重心的距离更新出新的重心的坐标是多少。然后新的成为钉子的点的坐标可以通过现在更新出的重心和他们初始时的角度关系来求出。
之后每次需要我们输出某个点的坐标,我们直接利用:
x’ = x + r i r_i ri * cos(rad + a n g l e i angle_i anglei),y’ = y + r i r_i ri * sin(rad + a n g l e i angle_i anglei)得到即可,其中rad代表此时重心相对于初始状态移动了多少角度。

关键就是要知道重心会和被固定的顶点在一条直线上,并且要掌握向量和点的旋转操作,然后就是上面忘了说求多边形的重心不能用( ∑ i = 1 n x i \sum_{i=1}^{n}{x_i} i=1nxi) / n 和 ( ∑ i = 1 n y i \sum_{i=1}^{n}{y_i} i=1nyi) / n来求,这样会损失很多的精度。应该把这个多边形剖分成多少三角形然后用公式G = ∑ i = 1 n ( S i ∗ g i ) \sum_{i=1}^{n}({S_i}*g_i) i=1n(Sigi)来得到,G代表多边形的重心,S是每一个分成的三角形面积,g是每个三角形的重心。

代码:

#include <bits/stdc++.h>

using namespace std;
const double eps = 1e-6;
const int MAXN = 1e4 + 7;
typedef long long ll;

int sign(double x)//判断符号可用
{
	if(fabs(x) <= eps) return 0;
	if(x > 0) return 1;
	else return -1;
}

struct Point
{
	double x,y;
	Point(){}
	//定义运算 
	Point(double _x,double _y){x = _x;y = _y;}
	Point operator + (const Point &b)const{
		return Point(x+b.x,y+b.y);
	}
	Point operator - (const Point &b)const{
		return Point(x-b.x,y-b.y);
	}
	Point operator * (const double &k)const{//乘常数
		return Point(x*k,y*k);
	}
	Point operator / (const double &k)const{
		return Point(x/k,y/k);
	}
	//点的叉积和点积都是数
	//点积
	double operator * (const Point &b)const{
		return x*b.x+y*b.y;
	}
	//叉积
	double operator ^ (const Point &b)const{
		return x*b.y-y*b.x;
	}

	Point base()const{//向量的方向向量
		if(sign(x) == -1 ||(sign(x) == 0 && sign(y) == -1))
			return Point(-x,-y);
		return *this;
	}

	bool operator < (const Point &a)const{//重载小于号 让map可以存向量 不然不可以直接存向量
		Point p1 = base(),p2 = a.base();
		return p1.x*p2.y < p2.y*p1.y;
	}

	double powlen(){return x*x+y*y;};

	double len(){return sqrt(powlen());}
};
typedef Point Vector;

double cross(Vector a,Vector b){//叉积
	return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){//点积
	return a.x*b.x+a.y*b.y;
}

Point p[MAXN];
int n,q;
double ag[MAXN],r[MAXN],rad;

int main(){
	scanf("%d%d",&n,&q);
	for(int i = 1;i <= n;i ++){
		scanf("%lf%lf",&p[i].x,&p[i].y);
	}
	double sum = 0,xg = 0,yg = 0;
	
	for(int i = 2;i < n;i ++){
		double tx,ty;
		tx = (p[1].x + p[i].x + p[i+1].x) / 3.0;
		ty = (p[1].y + p[i].y + p[i+1].y) / 3.0;
		double s = cross(p[i]-p[1],p[i+1]-p[1]);
		xg += tx * s;
		yg += ty * s;
		sum += s;
	}
	xg /= sum;
	yg /= sum;
//	cout<<xg<<' '<<yg<<endl;
	for(int i = 1;i <= n;i ++){
		ag[i] = atan2(p[i].y-yg,p[i].x-xg);
		r[i] = sqrt((p[i].x-xg)*(p[i].x-xg) + (p[i].y-yg)*(p[i].y-yg));
	}
	//获得了每个点相对于重心的角度和距离 可以通过 x' = x + r * cos(rad),y' = y + r * sin(rad)求出 
	int pin[2] = {1,2};
	//只需要知道每次旋转之后 钉子的位置 即可求出其他位置相对的答案
	double px[2] = {p[1].x,p[2].x};
	double py[2] = {p[1].y,p[2].y};
	
	int op,f,t,val;//val记录成为新的悬挂点的挂钩是哪个 
	while(q--){
		scanf("%d",&op);
		if(op == 1){
			scanf("%d%d",&f,&t);
			if(f == pin[0]) val = 1,pin[0] = t;
			else val = 0,pin[1] = t;
			xg = p[pin[val]].x;//新的重心位置 
			yg = p[pin[val]].y - r[pin[val]];
			rad = atan(1)*2.0 - ag[pin[val]];//π/ 2 - 相对角度 即为这个重心相对于以前转了多少度
			px[val^1] = xg + r[t] * cos(rad + ag[t]);
			py[val^1] = yg + r[t] * sin(rad + ag[t]); 
		}
		else{
			scanf("%d",&f);
			double tx,ty;
			tx = xg + r[f] * cos(rad + ag[f]);
			ty = yg + r[f] * sin(rad + ag[f]);
			printf("%.10f %.10f\n",tx,ty);
		}
	} 
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值