• hdu 6218 Bridge 线段树 set


    题目链接

    题意

    给一个(2)x(n)的矩阵,每个格子看成一个点,每个格子与相邻的格子间有边。现进行一些加边与删边操作,问每次操作后图中有多少条割边。

    思路

    参考

    https://www.cnblogs.com/rpSebastian/p/7834027.html

    割边

    在这个图中什么样的边才会是割边?情况貌似有点多。

    那么满足什么条件的边不会是割边?在环里面的边。

    的要求是什么?第一排和第二排对应位置都有边。

    环里面哪些边不是割边?找到这个环中最左边和最右边的竖边,夹起来的一整块里面所有的横边和竖边都不是割边。

    环外面可能有边不是割边吗?不可能。// 画一画图就知道了

    线段树

    由上面的讨论我们看到,对于一个环,要知道里面的非割边数目,就要知道这一块中

    1. 最左边和最右边的竖边;
    2. 里面的竖边的数目。

    于是可以用线段树维护竖边的信息。

    set

    那每个环怎么记录呢?

    用一个(set)记录上下都有边的一段段连续的边。

    (cnt[i])数组记录([i,i+1])一段的出现次数,取值只会是(0)(1)(2);当从(1)变成(2)或者从(2)变成(1)时更新(set)里维护的横边的信息,拆分或者合并。

    注意

    1. 每次操作输出答案,并不需要每次都遍历一遍(set)去对每个环统计一次答案,而只要在每次操作的时候同时计算这个操作的影响即可。
    2. 因为比较函数是根据左端点排序,所以如果当前要插入的段与set里已有的段左端点相同(之前的尚未删除),就不会被插入;因此要用multiset.

    Code

    #include <bits/stdc++.h>
    #define maxn 200010
    #define lson (rt<<1)
    #define rson (rt<<1|1)
    using namespace std;
    typedef long long LL;
    struct Tree { int l, r, lp, rp, cnt; }tr[maxn<<2];
    int num[maxn], ans;
    void build(int rt, int l, int r) {
        tr[rt].l = l, tr[rt].r = r; tr[rt].lp = l, tr[rt].rp = r, tr[rt].cnt = r-l+1;
        if (l == r) return;
        int mid = l+r >> 1;
        build(lson, l, mid), build(rson, mid+1, r);
    }
    void push_up(int rt) {
        tr[rt].cnt = tr[lson].cnt + tr[rson].cnt;
        tr[rt].lp = min(tr[lson].lp, tr[rson].lp);
        tr[rt].rp = max(tr[lson].rp, tr[rson].rp);
    }
    void modify(int rt, int p, int add) {
        if (tr[rt].l == tr[rt].r) {
            if (add) tr[rt].lp = tr[rt].rp = tr[rt].l, tr[rt].cnt = 1;
            else tr[rt].lp = maxn, tr[rt].rp = 0, tr[rt].cnt = 0;
            return;
        }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (p <= mid) modify(lson, p, add);
        else modify(rson, p, add);
        push_up(rt);
    }
    struct node2 { int l, r, cnt; };
    node2 ask(int rt, int l, int r) {
        if (tr[rt].l == l && tr[rt].r == r) {
            return {tr[rt].lp, tr[rt].rp, tr[rt].cnt};
        }
        int mid = tr[rt].l + tr[rt].r >> 1;
        if (r <= mid) return ask(lson, l, r);
        else if (l > mid) return ask(rson, l, r);
        else {
            node2 nl = ask(lson, l, mid), nr = ask(rson, mid+1, r);
            return {min(nl.l, nr.l), max(nl.r, nr.r), nl.cnt+nr.cnt};
        }
    }
    int ask(int l, int r) {
        node2 nd = ask(1, l, r);
        if (nd.l >= nd.r) return 0;
        return ((nd.r - nd.l) << 1) + nd.cnt;
    }
    struct node {
        int l, r;
        bool operator < (const node& nd) const { return l < nd.l; }
    };
    multiset<node> st;
    void del(int x) {
        --num[x];
        if (num[x]==0) return;
    
        auto p = st.upper_bound({x,x+1}); --p;
        ans += ask(p->l, p->r);
        if (p->l != x) ans -= ask(p->l, x), st.insert({p->l, x});
        if (p->r != x+1) ans -= ask(x+1, p->r), st.insert({x+1, p->r});
        st.erase(p);
    }
    void add(int x) {
        ++num[x];
        if (num[x]==1) return;
        auto pl = st.upper_bound({x,x+1}), pr = st.lower_bound({x,x+1}); --pl;
        if (pl->r==x && pr->l==x+1) {
            ans += ask(pl->l, pl->r);
            ans += ask(pr->l, pr->r);
            ans -= ask(pl->l, pr->r);
            st.insert({pl->l, pr->r});
            st.erase(pl), st.erase(pr);
        }
        else if (pl->r==x && pr->l!=x+1) {
            ans += ask(pl->l, pl->r);
            ans -= ask(pl->l, x+1);
            st.insert({pl->l, x+1});
            st.erase(pl);
        }
        else if (pl->r!=x && pr->l==x+1) {
            ans += ask(pr->l, pr->r);
            ans -= ask(x, pr->r);
            st.insert({x, pr->r}), st.erase(pr);
        }
        else {
            ans -= ask(x, x+1);
            st.insert({x,x+1});
        }
    }
    void work() {
        int n, m;
        scanf("%d%d", &n, &m);
        ans = 0;
        build(1, 1, n);
    
        st.clear();
        st.insert({1,n});
        st.insert({-1,-1});
        st.insert({n+2, n+2});
        for (int i = 1; i < n; ++i) num[i] = 2;
    
        while (m--) {
            int id,x1,y1,x2,y2;
            scanf("%d%d%d%d%d", &id, &x1, &y1, &x2, &y2);
            if (id==1) ++ans; else --ans;
            if (y1==y2) {
                auto p = st.upper_bound({y1,y1+1}); --p;
                bool flag = false;
                if (p->l<=y1 && p->r>=y1) flag = true;
                if (flag) ans += ask(p->l, p->r);
                if (id==1) modify(1, y1, 1);
                else modify(1, y1, 0);
                if (flag) ans -= ask(p->l, p->r);
            }
            else {
                if (id==2) del(min(y1,y2));
                else add(min(y1,y2));
            }
            printf("%d
    ", ans);
        }
    }
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) work();
        return 0;
    }
    
    
  • 相关阅读:
    模板方法模式
    结构型模式
    组合模式
    享元模式
    [STL离散化]Skyscrapers的lower_bound系列
    [水]浙大校赛补题
    [数]来自亮亮OJ的五道数学题
    [数]被数学淹没不知所措
    [tour]2019HUST onsite签到
    [树组BIT]训练两题重新理解ver.
  • 原文地址:https://www.cnblogs.com/kkkkahlua/p/7884384.html
Copyright © 2020-2023  润新知