ZOJ3511 Cake Robbery 解题报告 (线段树)


题目大意

链接

给定一个凸 N 边形,将其顶点按反时针顺序从 1 开始依次标号。现有 M 条连接其两个顶点 xy 的线段,保证他们在多边形内部两两不相交。问该多边形在被这 M 条线段划分以后边数最多的一个组件有多少条边。

题目保证 NM 不超过 10000 ,一共有不超过 20 组测试点。

分析

这个题几乎是我见过的最巧妙的线段树题目了。主要的难点在于它看起来跟线段树没有任何关系... 我也是瞄到了别人题解才往线段树的方向想的,但是至于怎么想到线段树我还是不知道... 不过观察这个题的以后应该可以得出的结论大概有两条

  1. 如果用几何方法的处理不好下手
  2. 10000 的数据规模看起来很诱人,但是因为多组测试点的存在,暴力很可能会超时

可能可以把这两条和那个奇怪的条件 『线段在多边形内部不相交』 作为切入点,然后往线段树的方向想

现在假设我们知道这个题用线段树可解。那么由于被切下来的部分也是凸多边形,所以我们可以把问题转化为组件的最大顶点数。从而对于每次划分,直接

query(L, R)reset(L + 1, R - 1)

即可。

不过这样会有一个问题,即如果某一片被切下来的组件还有后续的修改,那么将很难直接维护(需要追踪当前时刻的所有组件)。所幸我们发现,切割的顺序并不影响最终的答案。因此,我们只要先处理两个端点相隔较近的线段即可,不管怎么说每块的顶点数总不会越分越多。

我们这样似乎就完美地解决了这个问题。不过不要忘记最后处理完所有分割线以后对剩余的部分再做一次询问。

代码

总复杂度为 O(T×mlog(n))

#include <bits/stdc++.h>

using namespace std;

const int maxn = 11234;

struct Segment {
    int l, r, cnt;
} tree[maxn << 2];

int n, m;

void build(int o, int l, int r) {
    tree[o].l = l;
    tree[o].r = r;
    if (l == r) {
        tree[o].cnt = l <= n;
        return;
    }
    int m = l + r >> 1;
    build(o << 1, l, m);
    build(o << 1 | 1, m + 1, r);
    tree[o].cnt = tree[o << 1].cnt + tree[o << 1 | 1].cnt;
}

void update(int o, int l, int r) {
    if (!tree[o].cnt) return;
    if (l <= tree[o].l && tree[o].r <= r) {
        tree[o].cnt = 0;
        return;
    }
    int m = tree[o].l + tree[o].r >> 1;
    if (l <= m) update(o << 1, l, r);
    if (r > m) update(o << 1 | 1, l, r);
    tree[o].cnt = tree[o << 1].cnt + tree[o << 1 | 1].cnt;
}

int query(int o, int l, int r) {
    if (!tree[o].cnt) return 0;
    if (l <= tree[o].l && tree[o].r <= r) return tree[o].cnt;
    int m = tree[o].l + tree[o].r >> 1, ret = 0;
    if (l <= m) ret += query(o << 1, l, r);
    if (r > m) ret += query(o << 1 | 1, l, r);
    return ret;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    while (cin >> n >> m) {
        build(1, 1, 1 << 14);
        vector<pair<int, int>> cuts;
        while (m--) {
            int x, y;
            cin >> x >> y;
            if (x > y) swap(x, y);
            if (y - x < 2) continue;
            cuts.emplace_back(x, y);
        }
        sort(cuts.begin(), cuts.end(),
             [](pair<int, int> a, pair<int, int> b) { 
                 return a.second - a.first < b.second - b.first; });
        int ans = 0;
        for (auto now : cuts) {
            ans = max(ans, query(1, now.first, now.second));
            update(1, now.first + 1, now.second - 1);
        }
        ans = max(ans, query(1, 1, n));
        cout << ans << '\n';
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1. 矢量减法 设二维矢量 P = (x1,y1) ,Q = (x2,y2) 则矢量减法定义为: P - Q = ...
    潭潭_180阅读 6,756评论 0 1
  • ACM主要算法介绍 (以下是自己觉得比较好的算法学习的博客链接,自己做了部分顺序和分类调整)(以下算法分类来自于:...
    Dask_Jhonson阅读 9,260评论 0 0
  • 前言 多边形偏移 (polygon offset) 算法可能我们印象不深,不过用过 autoCAD 的同学应该有印...
    zyl06阅读 14,104评论 19 14
  • 黑云生骤雨,倾作漫天秋, 回望风萧瑟,红花共水流 。
    阿西莫多阅读 1,670评论 0 4
  • 魔幻神域 大型魔幻3D巨作,拥有唯美高清的游戏画面,丰富的游戏内容,多个奇幻探险世界,带给你与众不同的游戏体验。角...
    你眼里色彩斑斓阅读 1,216评论 0 0

友情链接更多精彩内容