• bzoj1453


    线段树+并查集

    这道题跟codeforces #416 div2 E几乎一样 于是我又抄了一遍代码

    我们建线段树,维护l->r区间这一块黑色白色连通块的数量,并且维护两端的并查集,每次合并区间就是把并查集合并,计算颜色的变化数量,并查集具体是维护一个fa数组,每次合并的时候把两端的并查集信息放到fa里,然后并查集操作是在fa上进行,线段树只是维护了一下信息。

    所以cf也抄原题?

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 210;
    struct node {
        int l[N], r[N];
        int ans[2];
    } tree[N << 3];
    int n, m;
    int fa[N * N], Map[N][N];
    #define id(x, y) (x - 1) * n + y  
    int find(int x) 
    {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    node merge(const node &a, const node &b, int mid) 
    {
        node c;
        for(int i = 1; i <= n; ++i) c.l[i] = a.l[i], c.r[i] = b.r[i], fa[a.l[i]] = a.l[i], fa[a.r[i]] = a.r[i], fa[b.l[i]] = b.l[i], fa[b.r[i]] = b.r[i];
        for(int i = 0; i < 2; ++i) c.ans[i] = a.ans[i] + b.ans[i];
        for(int i = 1; i <= n; ++i) if(Map[i][mid] == Map[i][mid + 1])
        {
            int u = find(a.r[i]), v = find(b.l[i]);
            if(u == v) continue;
            fa[u] = v;
            --c.ans[Map[i][mid]];
        }
        for(int i = 1; i <= n; ++i) c.l[i] = find(c.l[i]), c.r[i] = find(c.r[i]);
        return c;
    }
    void build(int l, int r, int x) 
    {
        if(l == r) 
        {
            for(int i = 1; i <= n; ++i) 
            {
                tree[x].l[i] = tree[x].r[i] = fa[id(i, l)] = id(i, l); 
                ++tree[x].ans[Map[i][l]];
            }        
            for(int i = 2; i <= n; ++i) if(Map[i][l] == Map[i - 1][l]) 
            {
                fa[id(i, l)] = tree[x].l[i] = tree[x].r[i] = fa[id(i - 1, l)];
                --tree[x].ans[Map[i][l]];
            }    
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, x << 1);
        build(mid + 1, r, x << 1 | 1);
        tree[x] = merge(tree[x << 1], tree[x << 1 | 1], mid);
    }
    void update(int l, int r, int x, int pos) 
    {
        if(l == r) 
        {
            tree[x].ans[0] = tree[x].ans[1] = 0;
            for(int i = 1; i <= n; ++i) 
            {
                tree[x].l[i] = tree[x].r[i] = fa[id(i, l)] = id(i, l); 
                ++tree[x].ans[Map[i][l]];
            }        
            for(int i = 2; i <= n; ++i) if(Map[i][l] == Map[i - 1][l]) 
            {
                fa[id(i, l)] = tree[x].l[i] = tree[x].r[i] = fa[id(i - 1, l)];
                --tree[x].ans[Map[i][l]];
            }            
            return;    
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) update(l, mid, x << 1, pos);
        else update(mid + 1, r, x << 1 | 1, pos);
        tree[x] = merge(tree[x << 1], tree[x << 1 | 1], mid);
    }
    int main()
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j) scanf("%d", &Map[i][j]);
        build(1, n, 1);
        scanf("%d", &m);
        while(m--) 
        {
            int x, y;
            scanf("%d%d", &x, &y);
            Map[x][y] ^= 1;
            update(1, n, 1, y);
            printf("%d %d
    ", tree[1].ans[1], tree[1].ans[0]); 
        }
        return 0;
    }
    View Code
  • 相关阅读:
    在单向链表中删除指定的key
    双向链表反转
    单向链表反转
    认识异或运算
    二分查找
    插入排序
    冒泡排序
    选择排序
    go 语言环境安装
    欧几里得算法
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7597323.html
Copyright © 2020-2023  润新知