返回顶部

SCUT - 151 - 小马哥和他的草坪 = 贪心

https://scut.online/p/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");
    }
}
posted @ 2019-10-16 22:40  Inko  阅读(109)  评论(0编辑  收藏  举报