BZOJ1453:[WC]Dface双面棋盘

浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html

题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1453

线段树维护行区间,每个结点只记录这个区间最上面一行和最下面一行每个格子在当前区间内的并查集情况,然后区间\(update\)的时候暴力合并就行了。因为除去最上面一行和最下面一行以外的格子都没有用,所以要保证并查集的代表元素是最上面一行或者最下面一行的。

时间复杂度:\(O(mnlogn)\)

空间复杂度:\(O(n^2)\)

代码如下:

#include <cstdio>
using namespace std;

int n,m;
int map[205][205];
int fa[805],tmp[805];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int find(int x) {
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

struct segment_tree {
    struct tree_node {
        int cnt[2],f[405];

        void init(int x) {
            f[1]=f[n+1]=1;int top=1;
            cnt[map[x][1]]=1,cnt[map[x][1]^1]=0;
            for(int i=2;i<=n;i++) {
                if(map[x][i]!=map[x][top])
                    cnt[map[x][i]]++,top=i;
                f[i]=f[i+n]=top;
            }
        }
    }tree[805];

    void update(int p,int x) {
        int ls=p<<1,rs=p<<1|1;
        for(int i=1;i<=n<<1;i++) {
            fa[i]=tree[ls].f[i];
            fa[i+(n<<1)]=tree[rs].f[i]+(n<<1);
        }
        tree[p].cnt[0]=tree[ls].cnt[0]+tree[rs].cnt[0];
        tree[p].cnt[1]=tree[ls].cnt[1]+tree[rs].cnt[1];
        for(int i=1;i<=n;i++)
            if(map[x][i]==map[x+1][i]&&find(i+n)!=find(i+(n<<1)))
                tree[p].cnt[map[x][i]]--,fa[fa[i+n]]=fa[i+(n<<1)];
        for(int i=1;i<=n<<2;i++) {
            find(i);
            if(i<=n)tmp[fa[i]]=i;
            if(i>n*3)tmp[fa[i]]=i-n*2;
        }
        for(int i=1;i<=n;i++) {
            tree[p].f[i]=tmp[fa[i]];
            tree[p].f[i+n]=tmp[fa[i+3*n]];
        }
    }

    void build(int p,int l,int r) {
        if(l==r) {tree[p].init(l);return;}
        int mid=(l+r)>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
        update(p,mid);
    }

    void change(int p,int l,int r,int pos) {
        if(l==r) {tree[p].init(l);return;}
        int mid=(l+r)>>1;
        if(pos<=mid)change(p<<1,l,mid,pos);
        else change(p<<1|1,mid+1,r,pos);
        update(p,mid);
    }

    void work() {
        int x=read(),y=read();
        map[x][y]^=1;
        change(1,1,n,x);
        printf("%d %d\n",tree[1].cnt[1],tree[1].cnt[0]);
    }
}T;

int main() {
    n=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            map[i][j]=read();
    T.build(1,1,n);m=read();
    for(int i=1;i<=m;i++)T.work();
    return 0;
}

转载于:https://www.cnblogs.com/AKMer/p/10218372.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值