[BZOJ3204][SDOI2013][线段树][并查集]城市规划

[Problem Description]
   
[Algorithm]
线段树 并查集
[Analysis]
写在最前面的话: 这真是我写过的最蛋疼的线段树!
求联通块,区间查询,点修改——线段树+并查集的节奏。很好……具体怎么做呢?真是一个蛋疼的问题……每个线段树节点维护这一片的联通块数量,和这一片两端边界的并查集以及每个集是否与某个O联通。处理宽度为1的时候的线段树节点,就是扫一遍维护一下并查集以及是否与O联通。合并两个儿子是麻烦的。需要将两个儿子的四个边界放到一起,然后通过相邻的那两条边界去更新并查集,同时一旦合并两个都与O联通的集,ans就减一。最后要更改并查集中的信息——因为最后留下来的只有两侧的边界,中间的那两个边界是不会出现的。所以将那些根节点位于中间的集的根更改一下。修改和查询与普通的线段树是一样的。
[Pay Attention]
内存要紧着点开……
[Code]
/**************************************************************
    Problem: 3204
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:2592 ms
    Memory:47276 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;
 
const int MAXN = 100010;
const int MAXM = 7;
const int HENG = 0;
const int SHU  = 1;
 
int n, m, q;
char map[MAXN][MAXM];
 
struct SegNode
{
    int cnt;
    int f[MAXM * 2], able[MAXM * 2];
    SegNode()
    {
        cnt = 0;
        memset(f, 0, sizeof(f));
        memset(able, 0, sizeof(able));
    }
} seg[MAXN * 4];
 
inline bool isok(char c, int ty)
{
    if (ty == HENG)
    {
        if (c == 'O' || c == '-' || c == '+') return true;
        else return false;
    }
    else
    {
        if (c == 'O' || c == '|' || c == '+') return true;
        else return false;
    }
}
 
int getfather(int *f, int now)
{
    return f[now] == now ? now : (f[now] = getfather(f, f[now]));
}
 
inline void DealwithSingle(int now, int h)
{
    int *t = seg[now].f;
    int *a = seg[now].able;
    for (int i = 1; i <= m; i++)
    {
        t[i] = i;
        a[i] = map[h][i] == 'O' ? 1 : 0;
    }
    for (int i = 1; i < m; i++)
        if (isok(map[h][i], HENG) && isok(map[h][i + 1], HENG))
        {
            t[i] = i + 1;
            a[i + 1] |= a[i];
        }
    seg[now].cnt = 0;
    bool check[MAXM] = {0};
    for (int i = 1; i <= m; i++)
    {
        int gf = getfather(t, i);
        if (!check[gf] && a[gf])
            seg[now].cnt++;
        check[gf] = true;
    }
    for (int i = m + 1; i <= 2 * m; i++)
        t[i] = i - m;
}
 
inline int update(int mid, int cnt1, int cnt2, int *f1, int *f2, int *a1, int *a2, int *ftar, int *atar)
{
    int f[MAXM * 4] = {0}, a[MAXM * 4] = {0};
    for (int i = 1; i <= m * 2; i++)
    {
        f[i] = f1[i];
        f[i + m * 2] = f2[i] + m * 2;
        a[i] = a1[i];
        a[i + m * 2] = a2[i];
    }
    int ans = cnt1 + cnt2;
    for (int i = 1; i <= m; i++)
    {
        if (isok(map[mid][i], SHU) && isok(map[mid + 1][i], SHU))
        {
            int sf = getfather(f, m + i);
            int gf = getfather(f, m * 2 + i);
            if (sf == gf) continue;
            if (a[sf] && a[gf]) ans--;
            f[gf] = sf;
            a[sf] |= a[gf];
        }
    }
    for (int i = 1; i <= m; i++)
    {
        ftar[i] = getfather(f, i);
        atar[i] = a[ftar[i]];
        ftar[i + m] = getfather(f, m * 3 + i);
        atar[i + m] = a[ftar[i + m]];
    }
    memset(f, 0, sizeof(f));
    for (int i = 1; i <= m; i++)
    {
        if (!f[ftar[i]]) f[ftar[i]] = i;
        if (!f[ftar[i + m]]) f[ftar[i + m]] = i + m;
    }
    for (int i = 1; i <= m; i++)
    {
        ftar[i] = f[ftar[i]];
        ftar[i + m] = f[ftar[i + m]];
    }
    return ans;
}
 
void build(int now, int left, int right)
{
    if (left == right)
    {
        DealwithSingle(now, left);
        return;
    }
    int mid = (left + right) >> 1;
    build(now << 1, left, mid);
    build((now << 1) + 1, mid + 1, right);
    seg[now].cnt = update(mid, seg[now << 1].cnt, seg[(now << 1) + 1].cnt,
                          seg[now << 1].f  , seg[(now << 1) + 1].f,
                          seg[now << 1].able, seg[(now << 1) + 1].able,
                          seg[now].f, seg[now].able);
}
 
void change(int now, int left, int right, int x)
{
    if (left == right)
    {
        DealwithSingle(now, left);
        return;
    }
    int mid = (left + right) >> 1;
    if (x <= mid)
        change(now << 1, left, mid, x);
    else
        change((now << 1) + 1, mid + 1, right, x);
    seg[now].cnt = update(mid, seg[now << 1].cnt, seg[(now << 1) + 1].cnt,
                          seg[now << 1].f  , seg[(now << 1) + 1].f,
                          seg[now << 1].able, seg[(now << 1) + 1].able,
                          seg[now].f, seg[now].able);
}
 
int query(int now, int left, int right, int l, int r, int *tf, int *ta)
{
    if (l <= left && right <= r)
    {
        memmove(tf + 1, seg[now].f + 1, sizeof(int) * m * 2);
        memmove(ta + 1, seg[now].able + 1, sizeof(int) * m * 2);
        return seg[now].cnt;
    }
    int mid = (left + right) >> 1;
    if (r <= mid)
        return query(now << 1, left, mid, l, r, tf, ta);
    else if (l > mid)
        return query((now << 1) + 1, mid + 1, right, l, r, tf, ta);
    else
    {
        int mlf[MAXM * 2], mrf[MAXM * 2], mla[MAXM * 2], mra[MAXM * 2];
        int cnt1 = query(now << 1, left, mid, l, r, mlf, mla);
        int cnt2 = query((now << 1) + 1, mid + 1, right, l, r, mrf, mra);
        return update(mid, cnt1, cnt2, mlf, mrf, mla, mra, tf, ta);
    }
}
 
int main()
{
    int tempf[MAXM * 2];
    int tempa[MAXM * 2];
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w" ,stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf(" %c", &map[i][j]);
    build(1, 1, n);
    scanf("%d", &q);
    for (int i = 1; i <= q; i++)
    {
        char order;
        scanf(" %c", &order);
        if (order == 'C')
        {
            int x, y;
            char k;
            scanf("%d%d %c", &x, &y, &k);
            map[x][y] = k;
            change(1, 1, n, x);
        }
        else
        {
            int left, right;
            scanf("%d%d", &left, &right);
            int ans = query(1, 1, n, left, right, tempf, tempa);
            printf("%d\n", ans);
        }
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值