1. 程式人生 > 實用技巧 >「LOJ#3146」「APIO2019」路燈

「LOJ#3146」「APIO2019」路燈

Description

一條 \(n\) 條邊,\(n+1\) 個點的鏈,邊有黑有白。若結點 \(a\) 可以到達 \(b\),需要滿足 \(a\to b\) 的路徑上的邊不能有黑的。現給出 \(0\) 時刻邊的初始狀態,然後隨後 \(1\sim q\) 時刻每時刻有一個事件或查詢:

  • \(\texttt{toggle} \ i\):翻轉第 \(i\) 條邊的顏色(黑 \(\Leftrightarrow\) 白)
  • \(\texttt{query}\ a\ b\):查詢從 \(0\) 開始到當前時刻,有多少時刻滿足 \(a\) 可以到達 \(b\)

對於每一個 \(\texttt{query}\)

,輸出答案。

Hint

\(1\le n, q\le 3\times 10^5\)

Solution

設一個位置 \(x\) 可以到達的最左側位置為 \(L(x)\),右側為 \(R(x)\)

這裡有一個 set 維護連續段 的技巧——對於邊狀態 \(\texttt{0100011}\),可以分段存為:\([1, 1], [2, 2], [3,5], [6, 7]\) 四段。對於一個位置 \(x\),所屬段為 \([l, r]\),那麼有 \(L(x)=l, R(x)= r\)。(好像可以直接線段樹)

當修改時,若將邊 \(x\to x+1\) 變為白色,那麼段 \([L(x), x],[x+1, R(x+1)]\)

間變為連通,可以將這兩段刪去,用 \([L(x), R(x+1)]\) 取而代之。對答案的影響?顯然所有滿足 \(a\in [L(x), x],b\in[x+1, R(x+1)]\) 的點對 \((a, b)\) 的答案都需要更改。

設當前時間為 \(t\),總時間為 \(T\)

考慮如何將一個點對轉化成一個二維點,而這又是本題的關鍵。這樣一來,上面的修改變成了 **矩形加 ** 操作——左下點為 \((L(x), x+1)\),右上為 \((x, R(x))\) 的矩形區域的每個位置加上 \(T-t\),表明 暫定後面時刻都是連通的

同理,若是白變黑,那麼就是矩形減 \(T-t\)表明目前看來,後面都不連通。

那麼對於查詢,只要獲取位置 \((a, b)\) 的值即可。若當前兩點是聯通的,由於上文是暫定,於是還要把後面減掉,答案 \(-(T-t)\)

如何高效執行上述兩個操作?顯然可以 CDQ 但我老寫炸。不如樹套樹吧。把矩形修改差分成四個,單點詢問轉化成區域查詢即可。

時空複雜度 \(O(n\log^2 n)\)

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem :  LOJ#3146 APIO2019 路燈
 */
#include <cstdio>
#include <set>

using namespace std;
const int MaxN = 3e5 + 5;

int n, N, T;
bool dat[MaxN];
char str[MaxN];

namespace bit_seg {
    const int S = MaxN << 9;
    int lc[S], rc[S], total = 0, sum[S];
    #define mid ((l + r) >> 1)

    int root[MaxN];
    #define lbt(x) (x & (-x))

    void ins(int& x, int p, int v, int l, int r) {
        if (!x) x = ++total;
        sum[x] += v;
        if (l == r) return;
        if (p <= mid) ins(lc[x], p, v, l, mid);
        else ins(rc[x], p, v, mid + 1, r);
    }
    void upd(int r, int c, int val) {
        for (; r <= n; r += lbt(r)) ins(root[r], c, val, 1, n + 1);
    }
    void rectAdd(int xl, int yl, int xr, int yr, int val) {
        upd(xl, yl, val);
        upd(xr + 1, yr + 1, val);
        upd(xl, yr + 1, -val);
        upd(xr + 1, yl, -val);
    }

    int get(int x, int ql, int qr, int l, int r) {
        if (!x) return 0;
        if (ql <= l && r <= qr) return sum[x];
        int ret = 0;
        if (l <= mid) ret += get(lc[x], ql, qr, l, mid);
        if (r > mid) ret += get(rc[x], ql, qr, mid + 1, r);
        return ret;
    }
    int Query(int r, int c) {
        int ret = 0;
        for (; r; r -= lbt(r)) ret += get(root[r], 1, c, 1, n + 1);
        return ret;
    }
};

struct interval {
    int l, r;
    inline interval(int L, int R) : l(L), r(R) { }
    inline bool operator < (const interval& x) const { return r < x.r; }
};
set<interval> itv;
typedef set<interval>::iterator Iter;

#include <algorithm>
signed main() {
    scanf("%d%d", &n, &T), N = n++;
    scanf("%s", str + 1);
    for (int i = 1; i <= n; i++)
        itv.insert(interval(i, i));
    
    for (int i = 1; i <= N; i++) {
        dat[i] = (str[i] == '1');
        if (dat[i]) {
            Iter it = --itv.lower_bound(interval(0, i + 1));
            int L = it->l;
            itv.erase(it), itv.erase(interval(i + 1, i + 1));
            itv.insert(interval(L, i + 1));
        }
    }

    for (Iter it = itv.begin(); it != itv.end(); it++)
        bit_seg::rectAdd(it->l, it->l, it->r, it->r, T);

    for (int t = 1; t <= T; t++) {
        char opt[10];
        int i, a, b;
        scanf("%s", opt);
        if (opt[0] == 't') {
            scanf("%d", &i);
            if (dat[i]) {
                Iter it = itv.lower_bound(interval(0, i));
                int l1 = it->l, r1 = i, l2 = i + 1, r2 = it->r;
                bit_seg::rectAdd(l1, l2, r1, r2, -(T - t));
                itv.erase(interval(l1, r2));
                itv.insert(interval(l1, r1));
                itv.insert(interval(l2, r2));
            } else {
                Iter it = itv.lower_bound(interval(0, i));
                int l1 = it->l, r1 = i, l2 = i + 1, r2 = (++it)->r;
                bit_seg::rectAdd(l1, l2, r1, r2, T - t);
                itv.erase(interval(l1, r1));
                itv.erase(interval(l2, r2));
                itv.insert(interval(l1, r2));
            }
            dat[i] ^= 1;
        } else {
            scanf("%d%d", &a, &b);
            int ans = bit_seg::Query(a, b);
            if (itv.lower_bound(interval(0, a)) == itv.lower_bound(interval(0, b)))
                printf("%d\n", ans - (T - t));
            else printf("%d\n", ans);
        }
    }
}