bzoj1573: [Usaco2009 Open]牛绣花cowemb

本文介绍了一种高效算法,用于解决给定多条直线与一圆相交时,找出所有相交于圆内的直线对。通过计算每条直线与圆的交点,并利用树状数组和离散化技术来统计相交对数。

题面在这里

明显不能暴力枚举两条直线。我们考虑怎样的情况会使得两条直线相交于圆内。如图:


只有这种情况:两条线与圆的4个交点相间排列。
故我们求出每条直线与圆的交点,把圆拉成一条线段,然后在线段上求交错的线段对数,树状数组+离散化即可。


/*************************************************************
	Problem: bzoj 1573 [Usaco2009 Open]牛绣花cowemb
	User: fengyuan
	Language: C++
	Result: Accepted
	Time: 204 ms
	Memory: 2788 kb
	Submit_Time: 2017-10-09 17:45:14
*************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define Pair pair<double, double>
#define mp make_pair
#define F first
#define S second
using namespace std;

const int N = 50010;
int n, cnt = 0, ans = 0;
double r, X1, X2, Y1, Y2, tmp, A, B, C, a, b, c, delta, L, R;
double inter[N<<1];
int T[N<<1];
Pair p[N];

inline void add(int k){ for (; k < N<<1; k += k&-k) T[k] ++; }
inline int getSum(int k){ int ret = 0; for (; k; k -= k&-k) ret += T[k]; return ret; }

inline double getPos(double x, double y){ if (y < 0) return -(r+x); else return r+x; }

int main()
{
	scanf("%d%lf", &n, &r);
	// x^2+y^2=r^2
	// ax+by+c=0
	for (int i = 1; i <= n; i ++){
		scanf("%lf%lf%lf", &a, &b, &c);
		bool mark = 0;
		if (a == 0 || b == 0){
			if (a == 0){
				Y1 = Y2 = -c/b;
				tmp = r*r-Y1*Y1;
				if (tmp < 0) mark = 1;
				else{ X1 = sqrt(tmp); X2 = -sqrt(tmp); }
			} else{
				X1 = X2 = -c/a;
				tmp = r*r-X1*X1;
				if (tmp < 0) mark = 1;
				else{ Y1 = sqrt(tmp); Y2 = -sqrt(tmp); }
			}
		} else{
			A = a*a+b*b; B = 2*b*c; C = c*c-a*a*r*r;
			delta = B*B-4*A*C;
			if (delta < 0) mark = 1;
			else{
				Y1 = (-B+sqrt(delta))/(2*A); X1 = (-c-b*Y1)/a;
				Y2 = (-B-sqrt(delta))/(2*A); X2 = (-c-b*Y2)/a;
			}
		}
		if (mark) continue;
		L = getPos(X1, Y1); R = getPos(X2, Y2);
		if (L > R) swap(L, R); p[++ cnt] = mp(L, R);
		inter[cnt*2-1] = L; inter[cnt*2] = R;
	}
	sort(inter+1, inter+1+2*cnt);
	sort(p+1, p+1+cnt);
	for (int i = 1; i <= cnt; i ++){
		int left = lower_bound(inter+1, inter+1+2*cnt, p[i].F) - inter;
		int right = lower_bound(inter+1, inter+1+2*cnt, p[i].S) - inter;
		ans += getSum(right) - getSum(left-1); add(right);
	}
	printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值