HDU-6392 Reverse Game(线段树+并查集)

Reverse Game

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

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

题意:有一个N*N的01矩阵,可以进行两种操作:①将一个格子的值与1取异或;②将一列格子的值与1取异或。每次操作完后,询问矩阵内有多少个0和1的联通块

题解:线段树+并查集

线段树每个叶子结点维护每一列的状态,对于每个节点,pl[x]和pr[x]表示最左端的并查集状态和最右端的并查集状态,合并区间的时候,将左边的pr[x]与右边的pl[x]合并,记录一下每个区间的联通块数量即可。

由于连通状态是变化的,不能普通地用并查集来算联通块。。。本想搞个可持久化并查集的,然后看了下std的写法。。。直接暴力重构并查集Orz。。。

然后复杂度就是O(MNlogN)

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'
#define eps 1e-8
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> VI;
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MX = 205;

int a[MX][MX], n;
int c[MX][MX];
int p[MX*MX], path[MX*MX], rear;
bool vis[MX*MX];

int find(int x) {
    if(p[x] == x) return x;
    if(!vis[x]) {
        vis[x] = 1;
        path[rear++] = x;
    }
    return p[x] = find(p[x]);
}
struct node {
    int pl[MX], pr[MX];
    int l, r, cnt[2];
} st[MX << 2], ret;

node operator+(const node& A, const node& B) {
    ret.cnt[0] = A.cnt[0] + B.cnt[0];
    ret.cnt[1] = A.cnt[1] + B.cnt[1];
    ret.l = A.l, ret.r = B.r;
    rep(i, 0, n) if(a[i][A.r] == a[i][B.l]) {
        int p1 = find(A.pr[i]), p2 = find(B.pl[i]);
        if(p1 != p2) {
            //fuck(i), fuck(p1), fuck(p2), fuck(a[i][A.r]), puts("");
            p[p1] = p2, ret.cnt[a[i][A.r]]--;
            if(!vis[p1]) path[rear++] = p1, vis[p1] = 1;
        }
    }
    rep(i, 0, n) ret.pl[i] = find(A.pl[i]);
    rep(i, 0, n) ret.pr[i] = find(B.pr[i]);
    return ret;
}
void init(node& np, int y) {
    np.l = np.r = y, np.cnt[0] = np.cnt[1] = 0;
    rep(i, 0, n) np.cnt[a[i][y]]++;
    rep(i, 0, n) p[c[i][y]] = np.pl[i] = np.pr[i] = c[i][y];
    rep(i, 1, n) if(a[i][y] == a[i - 1][y]) {
        np.pl[i] = np.pr[i] = np.pl[i - 1], np.cnt[a[i][y]]--;
    }
}
void build(int l, int r, int rt) {
    if(l == r) {
        init(st[rt], l);
        return;
    }
    int m = (l + r) >> 1;
    build(lson), build(rson);
    st[rt] = st[rt << 1] + st[rt << 1 | 1];
}
void update(int pos, int l, int r, int rt) {
    if(l == r) {
        rep(i, 0, n) a[i][pos] ^= 1;
        //fuck(pos);
        init(st[rt], pos);
        //fuck(l), fuck(r), fuck(st[rt].cnt[0]), fuck(st[rt].cnt[1]), puts("");
        return;
    }
    int m = (l + r) >> 1;
    if(pos <= m) update(pos, lson);
    else update(pos, rson);
    st[rt] = st[rt << 1] + st[rt << 1 | 1];
    //fuck(l), fuck(r), fuck(st[rt].cnt[0]), fuck(st[rt].cnt[1]), puts("");
}
void update(int x, int y, int l, int r, int rt) {
    if(l == r) {
        a[x][y] ^= 1;
        init(st[rt], y);
        //fuck(l), fuck(r), fuck(st[rt].cnt[0]), fuck(st[rt].cnt[1]), puts("");
        return;
    }
    int m = (l + r) >> 1;
    if(y <= m) update(x, y, lson);
    else update(x, y, rson);
    st[rt] = st[rt << 1] + st[rt << 1 | 1];
    //fuck(l), fuck(r), fuck(st[rt].cnt[0]), fuck(st[rt].cnt[1]), puts("");
}
int main() {
#ifdef local
    freopen("in.txt", "r", stdin);
#endif // local
    int T; cin >> T;
    while(T--) {
        cin >> n;
        rep(i, 0, n)rep(j, 0, n) p[c[i][j] = i * n + j] = i * n + j;
        rep(i, 0, n)rep(j, 0, n) scanf("%d", &a[i][j]);
        build(0, n - 1, 1);
        rep(j, 0, rear) p[path[j]] = path[j], vis[path[j]] = 0;
        rear = 0;


        int m, op, x, y, ans[2]; cin >> m;
        rep(i, 0, m) {
            scanf("%d", &op);
            if(op == 1) scanf("%d", &x), update(x - 1, 0, n - 1, 1);
            else scanf("%d%d", &x, &y), update(x - 1, y - 1, 0, n - 1, 1);
            rep(j, 0, 2) ans[j] = st[1].cnt[j];
            //fuck(ans[0]), fuck(ans[1]), puts("");
            rep(j, 0, n) if(a[j][0] == a[j][n - 1]) {
                int p1 = find(st[1].pl[j]), p2 = find(st[1].pr[j]);
                if(p1 != p2) {
                    p[p1] = p2, ans[a[j][0]]--;
                    if(!vis[p1]) path[rear++] = p1, vis[p1] = 1;
                }
            }
            rep(j, 0, rear) p[path[j]] = path[j], vis[path[j]] = 0;
            rear = 0;
            printf("%d %d\n", ans[0], ans[1]);
        }
    }


#ifdef local
    cout << "time cost:" << clock() << "ms" << endl;
#endif // local
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值