CF1167G题解

CF1167G题解

传送门

简化题意:数轴上有 n 个不相交且处于坐标为非负整数的单位正方形,给 m 个询问点,求出把这个点右侧的数轴逆时针旋转至与左侧相交时的角度。

首先,碰撞时只能有以下两种情况:

1.正方形与数轴碰撞。

2.正方形与正方形碰撞。

对于第一种情况,我们通过画图就可以很好理解求法:

情况1

答案就是 \(\arctan \dfrac{1}{d}\)

对于第二中情况,我们可以证明,两个正方形只会在顶点上相交。

情况二

采用反证法,如果两个正方形的交点 A 在其中一个的边上(显然不可能都在边上),那么过 A 向其所在的边所对的正方形的底边作垂线,并连接 A 与旋转中心 M ,我们就可以通过角平分线的性质证明出 \(\triangle AMH\) 全等于 \(\triangle AMB\) ,因此 \(BM=HM\) ,而 B,M 都是整点,因此 \(BM\) 长为整数,所以 \(HM\) 长为整数,H 在整点上,这也就证明了交点一定在正方形顶点上。

那满足什么条件时才会在顶点上相交呢?

结合下图和上图我们可以推出,当 \(\left|d1-d2\right|\leqslant1\) 时两个正方形会相交在顶点上,这时答案为 \(2\times\arctan \dfrac{1}{\max(d1,d2)}\)

数学的部分结束了,我们就开始考虑做法。

对于情况一,我们可以通过双指针 \(O(n)\) 求出所有答案。但对于情况二,我们考虑到情况一的答案最小为 \(\arctan \dfrac{1}{3500}\) ,这时情况二里 \(\max(d1,d2)\)\(7000\) ,也就是说要向两侧找最多 \(7000\) 个点,直接暴力找是 \(O(md)\) 的,过不了,但我们可以用 bitset 来维护每个询问点左右两侧 d 个点上是否有正方形,这样就可以把复杂度降到 \(O(\dfrac{nd}{w}+q)\) ,这样就可以通过此题。

#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
inline int read(){
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s;
}
const int N=200501;
const ld pi=acos(-1);//求π
int n,d,m,le,ri;
ll a[N],q[N];
bitset<7005>l,r,tmp;
ld work(int i,int j){
	return atan((ld)i/(ld)j);
}
int main(){
//	freopen("travel.in","r",stdin);
//	freopen("travel.out","w",stdout);
	n=read(),d=read();
	for(int i=1;i<=n;i++)a[i]=read();
	m=read();
	for(int i=1;i<=m;i++)q[i]=read();
	for(int i=1;i<=n&&a[i]<=q[1]+7000;i++){//处理第一个询问点前的情况
		if(a[i]>=q[1])r.set(a[i]-q[1]);
		else{
			le=i+1;
			if(a[i]>=q[1]-7001)l.set(q[1]-a[i]-1);
		}
	}
	for(int i=1;i<=m;i++){
		ld ans=0;
		int pos=0;
		if(l.test(0)||r.test(0))ans=pi/(ld)2.0;
		else ans=work(1,min(l._Find_first(),r._Find_first()));
		tmp=l&r;if(tmp.any())pos=tmp._Find_first(),ans=max(ans,pos?2*work(1,pos):pi);//d1=d2的情况
		tmp=(l<<1)&r;if(tmp.any())pos=tmp._Find_first(),ans=max(ans,2*work(1,pos));
		tmp=l&(r<<1);if(tmp.any())pos=tmp._Find_first(),ans=max(ans,2*work(1,pos));//|d1-d2|=1的情况
		printf("%.15Lf\n",ans);
		if(i==m)return 0;
		l<<=(q[i+1]-q[i]),r>>=(q[i+1]-q[i]);
		while(a[le]<q[i+1]){
			if(a[le]>=q[i+1]-7001)l.set(q[i+1]-a[le]-1);
			le++;
		}
		while(a[ri]<=q[i+1]+7000&&ri<=n){
			if(a[ri]>=q[i+1])r.set(a[ri]-q[i+1]);
			ri++;
		}
	}
}
posted @ 2023-02-12 19:02  Xttttr  阅读(28)  评论(0编辑  收藏  举报