题目链接
题目大意:
给定 n 个点,问从中能找出多少条线段,使剩余n-2个点到该线段的距离均不大于R。
前置知识:
点a到线段bc的距离,可以转换为点a到射线bc和射线cb距离的最大值。
解题思路:
要使线段p[i]p[j]到剩余n-2个点的距离均小于R,即满足射线p[i][j]到剩余n-2个点的距离小于等于R && 射线p[j]p[i]到剩余n-2个点的距离小于等于R。
而对于一个点来说,能满足其出发的射线到剩余n-1个点距离都小于等于R即求该点到剩余n-1个以R为半径的圆的两条切线的角度之交。
求出合法区间后就只需确认对于该点满足的区间内的点进行标记,最后符合答案的情况即:
if(mp[i][j] && mp[j][i]) Ans ++; //即设任意一点到该线段p[i][j]的距离为d //点到射线[p[i], p[j]) 的距离为d1 //点到射线[p[j], p[i]) 的距离为d2 //d = max(d1, d2) , 因为 d1 <= R && d2 <= R // 因此满足d <= R
代码如下:
#include#define In inline #define pi (atan(1.0)*4) #define enter puts("") #define MaxN 0x3f3f3f3f #define MinN 0xc0c0c0c0 #define pb push_back #define bug(x) cerr<<#x<<'='< =(b);i--) #define mset(a,b) memset(a,b,sizeof(a)) #define sz(a) (int)a.size() #define eps 1e-6 #define buff ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; template T gcd(T a,T b){return b?gcd(b,a%b):a;} template T lcm(T a,T b){return a*b/gcd(a,b);} typedef long long ll; typedef pair PII; const int mod = 998244353; const int MOD = 1e9+7; In ll read() { ll ans = 0; char ch = getchar(), las = ' '; while(!isdigit(ch)) las = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(las == '-') ans = -ans; return ans; } In void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x%10 + '0'); } struct Point{ double x, y; Point(){} Point(double x, double y):x(x), y(y){} void input(){scanf("%lf%lf", &x, &y);} Point operator + (Point b){return Point(x+b.x, y+b.y);} Point operator - (Point b){return Point(x-b.x, y-b.y);} double angle(Point b){ return atan2(b.y-y, b.x-x); } double dis(Point b){ return sqrt((x-b.x)*(x-b.x)+(y-b.y)*(y-b.y)); } }; const int N = 3100; int mp[N][N]; Point p[N]; void run_case(){ int n = read(), Rad = read(); rep(i, 1, n) p[i].input(); rep(i, 1, n){ double L = -pi, R = pi; bool ck = 1, flag = 1; rep(j, 1, n){ if(i != j){ double ang = p[i].angle(p[j]), r, l, d = p[i].dis(p[j]); if(d <= Rad) continue;//此时贡献限制区间为[-pi, pi], pass double a = asin(Rad*1.0/d); r = ang + a, l = ang - a; if(ck) L = l, R = r, ck = 0; else{ if(l > R + eps) l -= pi*2, r -= pi*2; if(r < L + eps) l += pi*2, r += pi*2; L = max(L, l), R = min(R, r); } if(L > R + eps){ flag = 0; break; }//合法区间不存在 } } if(flag){ rep(j, 1, n){ if(i == j) continue; double ang = p[i].angle(p[j]); if(ang > R + eps) ang -= 2 * pi; if(ang < L - eps) ang += 2 * pi; if(L - eps < ang && ang < R + eps) mp[i][j] = 1; } } } int Ans = 0; rep(i, 1, n) rep(j, i+1, n){ if(mp[i][j] && mp[j][i]) Ans ++; } write(Ans); enter; } int main() { // freopen("C:\Users\MARX HE\Desktop\input.txt","r",stdin); // freopen("C:\Users\MARX HE\Desktop\output.txt","w",stdout); int _ = 1; // int _ = read(); while(_ --){ run_case(); } }



