HDU - 6392 Reverse Game (2018 Multi-University Training Contest 7)(线段树 + 并查集)

Reverse Game

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 112    Accepted Submission(s): 52


 

Problem Description

One day, Umaru was enjoying Coke and potato chips as usual. Suddenly, an intellectual problem appeared on the television and the person who asked the problem first and correctly will get the latest《Jump》. Umaru really wants to get the 《Jump》so she told her brother the problem and begged him to help her. But her brother didn’t know how to do it, so he told you the problem and asked you for help.
There was a N×N matrix and each grid is black or white. The host reversed the color of the grid or reversed the color of the whole columns. Then the problem was how many white and black connected components. (If two grids have a common edge, we think they are connected.) To increase difficulty, the grid (i,1) and the grid (i,N) are connected. (1≤i≤N). 

 

 

Input

The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.
In each test case, the first line contains one integer N, which means the size of the matrix.
In the next N lines, each line contains N integers(0 or 1) means the color of each color. 1 represents black and 0 represents white.
Then the integer Q represents the number of reversals that the host does. Each line of the Q lines contains several numbers. If the first number is 1, the next one number y is the columns of the reversal. If the first number is 2, the next two numbers x and y represents the position of the reversal.
2≤N≤200
1≤Q≤20000

 

 

Output

The output contains Q lines. The i-th line contains two integers which represent the number of the white and black connected components after the first i operations

 

 

Sample Input

 

1 5 0 0 0 1 1 1 1 0 0 1 0 1 1 0 1 1 0 0 1 0 1 0 1 1 0 3 2 2 5 2 3 4 1 1

 

 

Sample Output

 

4 5 4 3 2 2

 

 

Source

2018 Multi-University Training Contest 7

 

 

Recommend

chendu

 

题意:给一个黑白棋盘(左右连通,相当于圆柱形),每次翻转一个格子,或者一列,问有多少黑色连通块和白色连通块。

题解:BZOJ1453 增加了翻转一列和左右连通的设定。基本上原题上稍微改改就做完了。所以去做原题吧!

https://blog.csdn.net/lzc504603913/article/details/82664321

 

#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int MAXN = 210;
typedef long long ll;

struct Node
{
    int L[MAXN], R[MAXN]; //当前节点,最左边和最右边的点分别是属于哪个集合的
    int ans[2];           //当前节点的答案
} tree[MAXN << 2];

int N, Q;

int fa[MAXN * MAXN];
int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int G[MAXN][MAXN];

//根据行列计算编号
int id(int x, int y)
{
    return (x - 1) * N + y;
}

//合并两个节点的操作,其实就是push_up,这里直接暴力计算!,一共会merrge计算log(N)次,所以查询复杂度为Nlog(N)
Node merge(Node a, Node b, int m)
{
    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]; //这里也要初始化,否则合并时会有问题。我们只关心tree[1],所以线段树的子节点怎么改都没问题,不影响最后答案
        fa[a.R[i]] = a.R[i];
        fa[b.L[i]] = b.L[i];
        fa[b.R[i]] = b.R[i];
    }

    c.ans[0] = a.ans[0] + b.ans[0];
    c.ans[1] = a.ans[1] + b.ans[1];

    for (int i = 1; i <= N; i++) //暴力更新!妙啊
    {
        if (G[i][m] == G[i][m + 1]) //不相等的话不用更新并查集了
        {
            int fx = find(a.R[i]);
            int fy = find(b.L[i]);
            if (fx != fy)
            {
                fa[fx] = fy;
                c.ans[G[i][m]]--;
            }
        }
    }
    for (int i = 1; i <= N; i++)
    {
        c.L[i] = find(c.L[i]); //保证L,R,必须是根,否则上面的初始化会有问题。
        c.R[i] = find(c.R[i]);
    }
    return c;
}

void build(int l, int r, int rt)
{
    if (l == r)
    {
        for (int i = 1; i <= N; i++)
        {
            tree[rt].L[i] = tree[rt].R[i] = fa[id(i, l)] = id(i, l);
            tree[rt].ans[G[i][l]]++;
        }
        for (int i = 2; i <= N; i++)
        {
            if (G[i][l] == G[i - 1][l])
            {
                fa[id(i, l)] = tree[rt].L[i] = tree[rt].R[i] = fa[id(i - 1, l)];
                tree[rt].ans[G[i][l]]--;
            }
        }
        return;
    }
    int m = (l + r) / 2;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    tree[rt] = merge(tree[rt << 1], tree[rt << 1 | 1], m);
}

void update(int C, int l, int r, int rt)
{
    if (l == r)
    {
        tree[rt].ans[0] = tree[rt].ans[1] = 0;
        for (int i = 1; i <= N; i++)
        {
            tree[rt].L[i] = tree[rt].R[i] = fa[id(i, l)] = id(i, l);
            tree[rt].ans[G[i][l]]++;
        }
        for (int i = 2; i <= N; i++)
            if (G[i][l] == G[i - 1][l])
            {
                fa[id(i, l)] = tree[rt].L[i] = tree[rt].R[i] = fa[id(i - 1, l)];
                tree[rt].ans[G[i][l]]--;
            }
        return;
    }
    int m = (l + r) / 2;
    if (C <= m)
        update(C, l, m, rt << 1);
    else
        update(C, m + 1, r, rt << 1 | 1);
    tree[rt] = merge(tree[rt << 1], tree[rt << 1 | 1], m);
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        memset(tree,0,sizeof(tree));

        scanf("%d", &N);
        for (int i = 1; i <= N; i++)
        {
            for (int j = 1; j <= N; j++)
            {
                scanf("%d", &G[i][j]);
            }
        }
        build(1, N, 1);
        scanf("%d", &Q);
        int op;
        int x, y;
        while (Q--)
        {
            scanf("%d", &op);
            if (op == 1)
            {
                scanf("%d", &x);
                for (int i = 1; i <= N; i++) //暴力更新图
                    G[i][x] ^= 1;

                update(x, 1, N, 1); //更新第X列节点

                for (int i = 1; i <= N; i++)//左右连通的情况,稍微计算下就好了
                {
                    if (G[i][1] == G[i][N])
                    {
                        int fx = find(tree[1].L[i]);
                        int fy = find(tree[1].R[i]);
                        if (fx != fy)
                        {
                            fa[fx] = fy;
                            tree[1].ans[G[i][1]]--;
                        }
                    }
                }
                printf("%d %d\n", tree[1].ans[0], tree[1].ans[1]);
            }
            else
            {
                scanf("%d%d", &x, &y);
                G[x][y] ^= 1;
                update(y, 1, N, 1); //更新第Y列节点
                for (int i = 1; i <= N; i++)
                {
                    if (G[i][1] == G[i][N])
                    {
                        int fx = find(tree[1].L[i]);
                        int fy = find(tree[1].R[i]);
                        if (fx != fy)
                        {
                            fa[fx] = fy;
                            tree[1].ans[G[i][1]]--;
                        }
                    }
                }

                printf("%d %d\n", tree[1].ans[0], tree[1].ans[1]);
            }
        }
    }

    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值