ZOJ3511 Cake Robbery 解題報(bào)告 (線(xiàn)段樹(shù))


題目大意

鏈接

給定一個(gè)凸 N 邊形,將其頂點(diǎn)按反時(shí)針順序從 1 開(kāi)始依次標(biāo)號(hào)?,F(xiàn)有 M 條連接其兩個(gè)頂點(diǎn) xy 的線(xiàn)段,保證他們?cè)诙噙呅蝺?nèi)部?jī)蓛刹幌嘟弧?wèn)該多邊形在被這 M 條線(xiàn)段劃分以后邊數(shù)最多的一個(gè)組件有多少條邊。

題目保證 NM 不超過(guò) 10000 ,一共有不超過(guò) 20 組測(cè)試點(diǎn)。

分析

這個(gè)題幾乎是我見(jiàn)過(guò)的最巧妙的線(xiàn)段樹(shù)題目了。主要的難點(diǎn)在于它看起來(lái)跟線(xiàn)段樹(shù)沒(méi)有任何關(guān)系... 我也是瞄到了別人題解才往線(xiàn)段樹(shù)的方向想的,但是至于怎么想到線(xiàn)段樹(shù)我還是不知道... 不過(guò)觀(guān)察這個(gè)題的以后應(yīng)該可以得出的結(jié)論大概有兩條

  1. 如果用幾何方法的處理不好下手
  2. 10000 的數(shù)據(jù)規(guī)??雌饋?lái)很誘人,但是因?yàn)槎嘟M測(cè)試點(diǎn)的存在,暴力很可能會(huì)超時(shí)

可能可以把這兩條和那個(gè)奇怪的條件 『線(xiàn)段在多邊形內(nèi)部不相交』 作為切入點(diǎn),然后往線(xiàn)段樹(shù)的方向想

現(xiàn)在假設(shè)我們知道這個(gè)題用線(xiàn)段樹(shù)可解。那么由于被切下來(lái)的部分也是凸多邊形,所以我們可以把問(wèn)題轉(zhuǎn)化為組件的最大頂點(diǎn)數(shù)。從而對(duì)于每次劃分,直接

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

即可。

不過(guò)這樣會(huì)有一個(gè)問(wèn)題,即如果某一片被切下來(lái)的組件還有后續(xù)的修改,那么將很難直接維護(hù)(需要追蹤當(dāng)前時(shí)刻的所有組件)。所幸我們發(fā)現(xiàn),切割的順序并不影響最終的答案。因此,我們只要先處理兩個(gè)端點(diǎn)相隔較近的線(xiàn)段即可,不管怎么說(shuō)每塊的頂點(diǎn)數(shù)總不會(huì)越分越多。

我們這樣似乎就完美地解決了這個(gè)問(wèn)題。不過(guò)不要忘記最后處理完所有分割線(xiàn)以后對(duì)剩余的部分再做一次詢(xún)問(wèn)。

代碼

總復(fù)雜度為 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';
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 矢量減法 設(shè)二維矢量 P = (x1,y1) ,Q = (x2,y2) 則矢量減法定義為: P - Q = ...
    潭潭_180閱讀 2,391評(píng)論 0 1
  • ACM主要算法介紹 (以下是自己覺(jué)得比較好的算法學(xué)習(xí)的博客鏈接,自己做了部分順序和分類(lèi)調(diào)整)(以下算法分類(lèi)來(lái)自于:...
    Dask_Jhonson閱讀 3,974評(píng)論 0 0
  • 前言 多邊形偏移 (polygon offset) 算法可能我們印象不深,不過(guò)用過(guò) autoCAD 的同學(xué)應(yīng)該有印...
    zyl06閱讀 12,209評(píng)論 19 14
  • 黑云生驟雨,傾作漫天秋, 回望風(fēng)蕭瑟,紅花共水流 。
    阿西莫多閱讀 256評(píng)論 0 4
  • 魔幻神域 大型魔幻3D巨作,擁有唯美高清的游戲畫(huà)面,豐富的游戲內(nèi)容,多個(gè)奇幻探險(xiǎn)世界,帶給你與眾不同的游戲體驗(yàn)。角...
    你眼里色彩斑斕閱讀 163評(píng)論 0 0

友情鏈接更多精彩內(nèi)容