【题解】Codeforces Round #573 (Div. 1) E. Tokitsukaze and Explosion

题目

其实官方题解已经非常清晰了。

主要是记录一下自己犯的错误。

最近要好好搞搞计算几何!

  1. 直接用角度区间判,并且还不用eps。可能1e5的坐标,acos,atan2之类的精读还行?
  2. 扩展成2 * n个时一定要保证右端点最大 - 最小 <= 2 * pi , 因为后面贪心是对右端点做得,所以不用考虑左端点。这样可以保证扩展后右端点还是递增的!
    3. 这个倍增的思路还是挺常见的。直接贪心做不了,用倍增加速求答案!
#include<bits/stdc++.h>
using namespace std;

typedef double ld;
const int maxn = 2e5 + 10;
const ld pi = acos(-1.0);
const ld inf = 1e12;
const ld eps = 1e-9;

struct node{
	ld l,r;
	bool operator < (node a)const{
		return r < a.r;
	}
	node operator + (ld a){
		return (node){l + a,r + a};
	}
}dt[maxn];
int f[maxn][20],n,m;
ld ang[maxn],X[maxn],Y[maxn],dis[maxn];
int tag;
bool check(ld mid){
	for (int i = 1 ; i <= n ; i++){
		ld t = acos(mid / dis[i]);
		dt[i] = (node){ang[i] - t,ang[i] + t};
		if ( dt[i].r > pi ) dt[i] = dt[i] + (-2 * pi); //只需要使得右端点差距不超过2 * pi即可,不用管左端点
	}

	sort(dt + 1,dt + n + 1);
	for (int i = 1 ; i <= n ; i++) dt[i + n] = dt[i] + 2 * pi;
	int N = 2 * n;
	for (int i = 1 ; i <= N ; i++){
		int cur = min(N,max(f[i - 1][0],i + 1));
		while ( cur < min(N,i + n) && dt[cur].l <= dt[i].r ) cur++;
		f[i][0] = cur;
		if ( tag ) cout<<i<<" "<<f[i][0]<<endl;
	}
	if ( tag ) for (int i = 1 ; i <= N ; i++) cout<<i<<" "<<dt[i].l<<" "<<dt[i].r<<endl;
	for (int j = 1 ; j < 20 ; j++) for (int i = 1 ; i <= N ; i++){
		f[i][j] = f[f[i][j - 1]][j - 1];
	}
	for (int i = 1 ; i <= n ; i++){
		int cur = 0 , id = i;
		for (int j = 19 ; j >= 0 ; j--){
			if ( f[id][j] < i + n ){
				cur += 1 << j;
				id = f[id][j];
			}
		}
		cur++;
		if ( tag) 	cout<<i<<" "<<cur<<endl;
		if ( cur <= m ) return 1;
	}
	return 0;
}
int main(){
	scanf("%d %d",&n,&m);
	cout<<setprecision(10);
	ld l = 0 , r = inf;
	for (int i = 1 ; i <= n ; i++){
		int x,y;
		scanf("%d %d",&x,&y);
		X[i] = x , Y[i] = y;
		dis[i] = sqrt(X[i] * X[i] + Y[i] * Y[i]) , r = min(r,dis[i]) , ang[i] = atan2(Y[i],X[i]);
	} 
	while ( (r - l) > eps ){
		ld mid = (l + r) / 2;
		if ( !check(mid) ) r = mid;
		else l = mid;
	}

//	cout<<l<<" "<<r<<endl;
	//printf("%.10lf\n",r);
	cout<<r<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值