2010-1146
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
== 题意 ==
魔理沙打妖精,魔炮的形状是二次项系数为a的抛物线,并且可以任意旋转。给定平面上n个妖精的坐标,问最多能打到多少个妖精。
== 题解 ==
规定一个起始的方向,抛物线从这个角度开始扫,转一圈后回到原来的位置。为了方便,这里我们用[-pi, pi]这个范围。对于每个点,求出点进入区域时抛物线的角度和离开区域时抛物线的角度。明显这两个角度是关于轴(0, 0)(x, y)对称的。轴(0, 0)(x,y)的角度alpha=atan2(y, x),则两个角度可以表示成 alpha - delta 和 alpha + delta 的形式。对于delta角,可以解方程x^2^ + y^2^ = (az^2^)^2^ + z^2^,解出z之后,delta = atan(z / (az^2^))。对每个点求出两个角度之后,将所有角度排序,算出初始位置角度等于-pi时,算出初始落在抛物线内点的个数cnt,然后扫一遍排好序的角度,遇到左端点(进入范围)则cnt++,遇到右端点(离开范围)则cnt--,取cnt变化过程中的最大值就是答案。
需要注意的是排序是先按角度排,如果角度相同则左端点排在右端点前面。头尾的情况很容易出错也需要特别注意。
== 代码 ==
{{{
#!cpp
#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>
#define PI acos(-1.0)
#define MAXN 30005
#define EPS 1e-8
using namespace std;
double x[MAXN], y[MAXN];
bool cmp(pair<double, int> p1, pair<double, int> p2) {
if(!(abs(p1.first - p2. first) < EPS))
return p1.first < p2.first;
else
return p1.second > p2.second;
}
int main() {
int n;
double a;
while(scanf("%d%lf", &n, &a) != EOF) {
double tx, ty, c, r, alpha, delta, le, ri;
vector<pair<double, int> > vp;
for(int i = 0; i < n; ++i) {
scanf("%lf", &x[i]);
}
for(int i = 0; i < n; ++i) {
scanf("%lf", &y[i]);
}
int cnt = 0;
for(int i = 0; i < n; ++i) {
tx = x[i];
ty = y[i];
c = -tx * tx - ty * ty;
r = (-1 + sqrt(1 - 4 * a * a * c)) / (2 * a * a);
r = sqrt(r);
alpha = atan2(ty, tx);
delta = atan(1.0 / (a * r));
le = alpha - delta, ri = alpha + delta;
if(le < -PI)
le += 2 * PI;
if(ri > PI)
ri -= 2 * PI;
if(le > ri)
++cnt;
vp.push_back(make_pair(le, 1));
vp.push_back(make_pair(ri, -1));
}
sort(vp.begin(), vp.end(), cmp);
int maxcnt = cnt;
vector<pair<double, int> >::iterator it;
for(it = vp.begin(); it != vp.end(); ++it) {
cnt += it->second;
if(cnt > maxcnt)
maxcnt = cnt;
}
printf("%d daze\n", maxcnt);
}
return 0;
}
}}}
题意
魔理沙打妖精,魔炮的形状是二次项系数为a的抛物线,并且可以任意旋转。给定平面上n个妖精的坐标,问最多能打到多少个妖精。
题解
规定一个起始的方向,抛物线从这个角度开始扫,转一圈后回到原来的位置。为了方便,这里我们用[-pi, pi]这个范围。对于每个点,求出点进入区域时抛物线的角度和离开区域时抛物线的角度。明显这两个角度是关于轴(0, 0)(x, y)对称的。轴(0, 0)(x,y)的角度alpha=atan2(y, x),则两个角度可以表示成 alpha - delta 和 alpha + delta 的形式。对于delta角,可以解方程x2 + y2 = (az2)2 + z2,解出z之后,delta = atan(z / (az2))。对每个点求出两个角度之后,将所有角度排序,算出初始位置角度等于-pi时,算出初始落在抛物线内点的个数cnt,然后扫一遍排好序的角度,遇到左端点(进入范围)则cnt++,遇到右端点(离开范围)则cnt--,取cnt变化过程中的最大值就是答案。
需要注意的是排序是先按角度排,如果角度相同则左端点排在右端点前面。头尾的情况很容易出错也需要特别注意。
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>
#define PI acos(-1.0)
#define MAXN 30005
#define EPS 1e-8
using namespace std;
double x[MAXN], y[MAXN];
bool cmp(pair<double, int> p1, pair<double, int> p2) {
if(!(abs(p1.first - p2. first) < EPS))
return p1.first < p2.first;
else
return p1.second > p2.second;
}
int main() {
int n;
double a;
while(scanf("%d%lf", &n, &a) != EOF) {
double tx, ty, c, r, alpha, delta, le, ri;
vector<pair<double, int> > vp;
for(int i = 0; i < n; ++i) {
scanf("%lf", &x[i]);
}
for(int i = 0; i < n; ++i) {
scanf("%lf", &y[i]);
}
int cnt = 0;
for(int i = 0; i < n; ++i) {
tx = x[i];
ty = y[i];
c = -tx * tx - ty * ty;
r = (-1 + sqrt(1 - 4 * a * a * c)) / (2 * a * a);
r = sqrt(r);
alpha = atan2(ty, tx);
delta = atan(1.0 / (a * r));
le = alpha - delta, ri = alpha + delta;
if(le < -PI)
le += 2 * PI;
if(ri > PI)
ri -= 2 * PI;
if(le > ri)
++cnt;
vp.push_back(make_pair(le, 1));
vp.push_back(make_pair(ri, -1));
}
sort(vp.begin(), vp.end(), cmp);
int maxcnt = cnt;
vector<pair<double, int> >::iterator it;
for(it = vp.begin(); it != vp.end(); ++it) {
cnt += it->second;
if(cnt > maxcnt)
maxcnt = cnt;
}
printf("%d daze\n", maxcnt);
}
return 0;
}