[洛谷] P3268 圆的异或并(扫描线)

传送门: 圆的异或并

思路:
设最外层的圆的层级为 1 级,根据容斥关系不难得出最终所求的面积为:奇数层的圆面积之和 - 偶数层的面积之和,所以关键是如何求每个圆的层级。

假设我们在图上任意画一条平行于 x 轴的直线,与图上部分圆相交,这些交点一定是成对出现的,而且由于题目中的圆一定不相交,这些成对的点之间一定是满足括号嵌套关系的,我们将所有的交点按 x 排序:

  1. 如果某圆 A 的左交点的左侧是某圆 B 的右交点,那么 A 与 B 同层级(如果 B 的交点存在,则其层级一定已经求出)
  2. 如果某圆 A 的左交点的左侧是某圆 B 的左交点,那么 A 的层级比 B 多一层
  3. 特殊情况,A 的左交点的左侧没有其它交点,说明 A 在最外层,层级为 1

于是,我们可以先将每个圆的上下顶点保存并按 y 排序,再用一条平行于 x 轴的扫描线从下往上扫描:

  1. 如果遇到圆的下顶点,则将圆的左右交点加入 set 中,set 按 x 排序
  2. 如果遇到圆的上顶点,则将圆的左右交点从 set 中删去,即删除该圆

交点的坐标不能直接存圆的左右顶点,这样层级会出错,需要用当前扫描线位置实时计算。由于圆不会相交,所以已存入的交点间的关系是不会改变的,实时计算的交点坐标只会影响新插入的点的位置。

还有一个需要注意的点是插入时左右交点的坐标是相同的,如果不做处理第二个交点就无法插入 set 中,这一点很好处理,将左交点向左挪 eps,右交点向右挪 eps 即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef tuple<ll, ll, ll> tup;
#define fsio ios::sync_with_stdio(false)
const int inf = 0x3f3f3f3f;
const int maxn = 5e5+10;
const double eps = 1e-8;

int nowy, sg[maxn];
tup s[maxn];

struct node
{
    ll y, id;
    bool ud;
    bool operator < (const node &b) const
    {
        return y < b.y;
    }
}p[maxn];

struct node2
{
    ll id;
    bool lr;
    double nowx() const
    {
        auto [x, y, r] = s[id];
        if (lr) return (double)x - (double)sqrt(r*r - (y-nowy)*(y-nowy)) - eps;
        else return (double)x + (double)sqrt(r*r - (y-nowy)*(y-nowy)) + eps;
    }
    bool operator < (const node2 &b) const
    {
        return nowx() < b.nowx();
    }
};

int main()
{
    int n;
    while(cin>>n)
    {
        for (int i = 0; i < n; i++)
        {
            ll x, y, r;
            cin>>x>>y>>r;
            s[i] = {x, y, r};
            p[i] = {y-r, i, 0};
            p[n+i] = {y+r, i, 1};
        }
        sort(p, p+2*n);
        set<node2> st;
        ll ans = 0;
        for (int i = 0; i < 2*n; i++)
        {
            ll y = p[i].y, id = p[i].id, r = get<2>(s[id]);
            bool ud = p[i].ud;
            nowy = y;
            if (ud)
            {
                st.erase({id, 1});
                st.erase({id, 0});
            }
            else
            {
                st.insert({id, 0});
                set<node2>::iterator it = st.insert({id, 1}).first;
                if (it == st.begin())
                {
                    sg[id] = 1;
                }
                else
                {
                    it--;
                    if (it->lr) sg[id] = -sg[it->id];
                    else sg[id] = sg[it->id];
                }
                ans += sg[id]*r*r;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
posted @ 2022-07-19 14:21  Pannta  阅读(44)  评论(0编辑  收藏  举报