1 条题解

  • 0
    @ 2025-8-24 22:24:17

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar jiangly
    这个家伙很菜,什么也没有留下

    搬运于2025-08-24 22:24:17,当前版本为作者最后更新于2020-09-19 12:22:19,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    首先可以扫描线 + 线段树求出每个询问四个方向的第一个点,通过这些点把询问拆成四个方向的凸包。

    预处理每对点逆时针沿凸包走的叉积和 (如左下到右上就是沿右下凸包走)。每个询问将四个方向求和再除以 22 即可。

    时间复杂度 O(n2+mlogm)O(n^2+m\log m)

    #include <bits/stdc++.h>
    
    constexpr int N = 3e3, M = 1e6, S = 1 << 21;
    
    int n, m, k;
    struct Point {
        int x, y;
        Point(int x = 0, int y = 0) : x(x), y(y) {}
    } p[N];
    bool operator<(const Point &lhs, const Point &rhs) {
        return lhs.x != rhs.x ? lhs.x < rhs.x : lhs.y < rhs.y;
    }
    int64_t cross(const Point &lhs, const Point &rhs) {
        return 1ll * lhs.x * rhs.y - 1ll * lhs.y * rhs.x;
    }
    Point operator-(const Point &lhs, const Point &rhs) {
        return Point(lhs.x - rhs.x, lhs.y - rhs.y);
    }
    
    int a[N];
    int64_t f[N][N];
    
    int xl[M], xr[M], yl[M], yr[M];
    int b[M];
    int pt[M][4];
    
    int lg;
    int arr[S];
    
    void modify(int x, int v) {
        for (x += (1 << lg) + 1; x; x /= 2)
            arr[x] = std::min(arr[x], v);
    }
    
    int rangeMin(int l, int r) {
        int res = n;
        for (l += (1 << lg), r += (1 << lg) + 2; l ^ r ^ 1; l /= 2, r /= 2) {
            if (l % 2 == 0)
                res = std::min(res, arr[l ^ 1]);
            if (r % 2 == 1)
                res = std::min(res, arr[r ^ 1]);
        }
        return res;
    }
    
    void work(int id, int offset = 0) {
        std::iota(a, a + n, 0);
        std::sort(a, a + n, [&](int i, int j) {
            return p[i] < p[j];
        });
        
        std::iota(b, b + m, 0);
        std::sort(b, b + m, [&](int i, int j) {
            return xl[i] > xl[j];
        });
        
        std::fill(arr, arr + (1 << (lg + 1)), n);
        
        for (int i = n - 1, qi = 0; i >= 0; --i) {
            int u = a[i];
            int w = u;
            for (int j = i + 1; j < n; ++j) {
                int v = a[j];
                if (p[v].y < p[u].y)
                    continue;
                if (cross(p[w] - p[u], p[v] - p[u]) <= 0)
                    w = v;
                f[u][v] = cross(p[u], p[w]) + f[w][v];
            }
            
            modify(p[u].y + offset, i);
            
            int lim = i == 0 ? -k - 1 : p[a[i - 1]].x;
            while (qi < m && xl[b[qi]] > lim) {
                int j = b[qi];
                pt[j][id] = a[rangeMin(yl[j] + offset, yr[j] + offset)];
                ++qi;
            }
        }
    }
    
    void rotate() {
        for (int i = 0; i < n; ++i) {
            std::swap(p[i].x, p[i].y);
            p[i].x = -p[i].x;
        }
        for (int i = 0; i < m; ++i) {
            std::swap(xl[i], yl[i]);
            std::swap(xr[i], yr[i]);
            std::swap(xl[i], xr[i]);
            xl[i] = -xl[i];
            xr[i] = -xr[i];
        }
    }
    
    int main() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        
        std::cin >> k >> n;
        while ((1 << lg) < k + 3)
            ++lg;
        for (int i = 0; i < n; ++i)
            std::cin >> p[i].x >> p[i].y;
        std::cin >> m;
        for (int i = 0; i < m; ++i)
            std::cin >> xl[i] >> xr[i] >> yl[i] >> yr[i];
        
        work(0);
        rotate();
        work(3);
        rotate();
        work(2, k);
        rotate();
        work(1, k);
        
        for (int i = 0; i < m; ++i) {
            int64_t area = 0;
            for (int j = 0; j < 4; ++j)
                area += f[pt[i][j]][pt[i][(j + 1) % 4]];
            std::cout << area / 2 << "." << "05"[area % 2] << "\n";
        }
        
        return 0;
    }
    
    • 1

    信息

    ID
    5880
    时间
    5000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者