SCUT - 151 - 小马哥和他的草坪 = 贪心
一块长方形草坪,中线上有一群圆心,可以打开圆心的开关给长方形草坪浇水,求覆盖整个长方形需要的最少的开关。
一开始不知道怎么处理这些边界点……
但其实观察到一个事实,首先它是对称的,只考虑上半部分。每个上边界的点被覆盖,则它的垂线必被覆盖。故很显然就需要覆盖最上方的边界。
所以就是给一堆线段,求最少的线段把上边界完全覆盖。按右边界排序之后贪心就可以了。
但实际上确实蛮多细节的。尤其是离散化还有最后一个点的覆盖问题。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double p[200005];
double l[100005];
double r[100005];
int n;
pair<int, int> LR[100005];
vector<int> st;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
double X, Y;
while(~scanf("%lf%lf", &X, &Y)) {
st.clear();
Y /= 2.0;
scanf("%d", &n);
double x, y;
int sucl = 0;
int sucr = 0;
int cnt = 0;
for(int i = 1; i <= n; ++i) {
scanf("%lf%lf", &x, &y);
if(y <= Y)
continue;
++cnt;
double dx = sqrt(y * y - Y * Y);
l[cnt] = round((x - dx) * 1e10) / 1e10;
l[cnt] = max(0.0, l[cnt]);
r[cnt] = round((x + dx) * 1e10) / 1e10;
r[cnt] = min(X, r[cnt]);
p[cnt * 2 - 1] = l[cnt];
p[cnt * 2] = r[cnt];
//cout <<"l="<< l[cnt] << endl;
//cout <<"r="<< r[cnt] << endl;
if(l[cnt] == 0.0)
sucl = 1;
if(r[cnt] == X)
sucr = 1;
}
if(cnt == 0 || sucl == 0 || sucr == 0) {
puts("-1");
continue;
}
sort(p + 1, p + 1 + cnt * 2);
int tcnt = unique(p + 1, p + 1 + cnt * 2) - (p + 1);
for(int i = 1; i <= cnt; ++i) {
LR[i].first = lower_bound(p + 1, p + 1 + tcnt, l[i]) - p;
LR[i].second = lower_bound(p + 1, p + 1 + tcnt, r[i]) - p;
//cout << "L="<<LR[i].first << " R=" << LR[i].second << endl;
}
//线段按L顺序排序
sort(LR + 1, LR + 1 + cnt);
int cur = 0;
int sum = 0;
int suc = 1;
int LRi = 1;
//整个线段是[1,tcnt],结束点不考虑
for(int i = 1; i < tcnt; ++i) {
//加入覆盖点i的线段
while(LRi <= cnt && i >= LR[LRi].first) {
//单调栈,存R点最大的若干个
while(st.size() && st.back() <= LR[LRi].second)
st.pop_back();
st.push_back(LR[LRi].second);
++LRi;
}
//离开i点弹出在i点结束的点
while(st.size() && st.back() <= i)
st.pop_back();
//当前点没有被覆盖
if(cur <= i) {
if(st.empty()) {
suc = 0;
break;
} else {
//选最远的一个点进行覆盖
cur = st[0];
++sum;
}
}
//printf("i=%d cur=%d\n",i,cur);
}
//强制算上结束点
if(cur < tcnt)
sum++;
if(suc)
printf("%d\n", sum);
else
puts("-1");
}
}