bzoj1453[Wc]Dface双面棋盘

一个$n*n$的棋盘,每个格子为黑色或白色。m次操作,每次修改一个格子的颜色,计算当前有多少黑色四联通块和白色四联通块

$$1 leq n leq  200$$

$$1 leq m leq 10000$$

当年科技不发达...这明明是LCT一眼题啊喂

设有$x_1$个原来与当前格子同色的连通块与当前格子断开,$x_2$个原来与当前格子不同色的连通块与当前格子连通,那么会增加$x_1-1$个当前格子原来颜色的连通块,减少$x_2-1$个当前格子现在颜色的连通块。 

然后我们可以离线然后写一个边权LCT艹过去...

时间复杂度$O((n+m)log_2n)$

但是机房里的小朋友们估计连水管局长数据加长版$[bzoj2594]$都不会做...

于是还是讲一下这道题的在线做法吧

考虑线段树的奇怪用法(参照)

线段树上对应$[l,r]$的节点表示棋盘第$l$行到第$r$行有多少黑色联通块和白色联通块

同时由于这道题是四联通,有可能隔开的两个格是联通的

会出现 黑 白 黑
            黑 黑 黑

这种情况,所以我们考虑用并查集合并两个区间,每个点可以挂一个并查集,每次$O(n)$合并一下

时间复杂度$O(n^3+m*nlogn)$

Merge的时候小细节:并查集数组开4倍 用

$1 cdots n$表示上方

$n+1 cdots 2n$表示下方

$1 cdots n$表示上方

$2n+1 cdots 4n$表示右边上下方

爆搞一下

(但是依然钟情于LCT的我是不会写线段树套并查集的w

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
 
struct node
{
    int b, w, f[808];
    void init (int n)
    {
        b = w = 0;
        for (int i = 0; i <= n; i++)
        f[i] = i;
    }
    int find (int x)
    {
        if (f[x] == x) return x;
        return f[x] = find(f[x]);
    }
    void merge (int a, int b)
    {
        f[find(a)] = find(b);
    }
};
 
int n, m;
bool map[205][205];
 
struct SegmentTree
{
    node T[808];
    #define LChild p<<1,l,mid
    #define RChild p<<1|1,mid+1,r
 
    void maintain (int p, int l, int r)
    {
        int lc = p<<1, rc = lc|1, mid = (l+r)/2;
        T[p].init(n<<2);
        T[p].b = T[lc].b + T[rc].b;
        T[p].w = T[lc].w + T[rc].w;
        for (int i = 1; i <= n; i++)
        T[p].f[i] = T[lc].f[i],
        T[p].f[i+n] = T[lc].f[i+n],
        T[p].f[i+n+n] = T[rc].f[i]+n*2,
        T[p].f[i+n+n+n] = T[rc].f[i+n]+n*2;
        for (int i = 1; i <= n; i++)
        if (map[mid][i]==map[mid+1][i])
        {
            int f1 = T[p].find(i+n);
            int f2 = T[p].find(i+n+n);
            if (f1 != f2)
            {
                T[p].w -= (map[mid][i]==0), 
                T[p].b -= (map[mid][i]==1);
                T[p].merge(f1,f2);
            }
        }
        for (int i = 1; i <= n; i++)
        {
            int fi = T[p].find(i);
            if (fi > n) T[p].f[fi] = T[p].f[i] = i;
        }
        for (int i = n*3+1; i <= n*4; i++)
        {
            int fi = T[p].find(i);
            if (fi <= n) T[p].f[i-n*2] = T[p].f[i];
            else T[p].f[fi] = T[p].f[i-n*2] = i-n*2;
        }
    }
 
    void make (int r, int p)
    {
        T[p].init(n<<2);
        for (int i = 1; i <= n; i++)
        T[p].merge(i,i+n);
        for (int i = 2; i <= n; i++)
        if (map[r][i]==map[r][i-1]) 
        {
            T[p].merge(i,i-1);
            T[p].merge(i+n,i+n-1);
        }
        for (int i = 1; i <= n; i++)
        if (T[p].f[i] == i) 
        T[p].w += map[r][i]==0, T[p].b += map[r][i]==1;
        for (int i = 1; i <= n; i++)
        if (T[p].f[i+n] == i+n) 
        T[p].w += map[r][i]==0, T[p].b += map[r][i]==1;
    }
 
    void build (int p, int l, int r)
    {
        if (l == r) make(r,p);
        else
        {
            int mid = (l+r)/2;
            build(LChild);
            build(RChild);
            maintain(p,l,r);
        }
    }
 
    void change (int p, int l, int r, int pos)
    {
        if (l == r) {make(r,p);return;}
        int mid = (l+r)/2;
        if (pos <= mid) change(LChild,pos);
        else change(RChild,pos);
        maintain(p,l,r);
    }
 
    void change (int x, int y)
    {
        map[x][y] = !map[x][y];
        change (1, 1, n, x);
    }
 
    void query ()
    {
        printf ("%d %d
", T[1].b, T[1].w);
    }
 
}Solve;
 
int main ()
{
    scanf ("%d", &n);
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
    scanf ("%d", map[i]+j);
 
    Solve.build(1,1,n);
    scanf ("%d", &m);
    for (int i = 1; i <= m; i++)
    {
        int x, y; scanf ("%d %d", &x, &y);
        Solve.change(x,y);
        Solve.query();
    }
    return 0;
}
线段树套并查集
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
 
int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};
int n,m,sz,color[205][205],c[205][205],cnt;
struct data
{
    int x,y,t,d,id,opt,c;
    bool operator < (const data &a) const
    {
        return x<a.x||x==a.x&&y<a.y;
    }
}e[200005],opr[10005];
map <data,int> mp;
int f[200005],ch[200005][2],minn[200005],rev[200005],stack[200005],val[200005];
int re[200005],pt[200005],l[200005],r[200005],tree[200005];
int ans[2];
 
int read()
{
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
//---------------------------init----------------------------------
int eid(int a,int b,int c,int d)
{
    if (a==c)
    {
        if (b>d) swap(b,d);
        return (a-1)*(n-1)+b;
    }
    else
    {
        if (a>c) swap(a,c);
        return n*(n-1)+(a-1)*n+b;
    }
}
int cmptopt(data a,data b)
{
    return a.t<b.t||(a.t==b.t&&a.opt<b.opt);
}
//----------------------------lct----------------------------------
bool isroot(int x)
{
    return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
int get(int x)
{
    return ch[f[x]][1]==x;
}
void update(int x)
{
    int loc=x;
    if (ch[x][0])
    {
        if (val[minn[ch[x][0]]]<val[loc])
            loc=minn[ch[x][0]];
    }
    if (ch[x][1])
    {
        if (val[minn[ch[x][1]]]<val[loc])
            loc=minn[ch[x][1]];
    }
    minn[x]=loc;
}
void pushdown(int x)
{
    if (x&&rev[x])
    {
        if (ch[x][0]) rev[ch[x][0]]^=1;
        if (ch[x][1]) rev[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
        rev[x]=0;
    }
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],wh=get(x);
    if (!isroot(old)) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    ch[old][wh]=ch[x][wh^1];
    if (ch[old][wh]) f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    update(old);
    update(x);
}
void splay(int x)
{
    int top=0;stack[++top]=x;
    for (int i=x;!isroot(i);i=f[i]) stack[++top]=f[i];
    for (int i=top;i;--i) pushdown(stack[i]);
 
    for (int fa;!isroot(x);rotate(x))
        if (!isroot(fa=f[x]))
            rotate((get(x)==get(fa))?fa:x);
}
void access(int x)
{
    int t=0;
    for (;x;t=x,x=f[x])
    {
        splay(x);
        ch[x][1]=t;
        update(x);
    }
}
void reverse(int x)
{
    access(x);
    splay(x);
    rev[x]^=1;
}
int find(int x)
{
    access(x);
    splay(x);
    while (ch[x][0]) x=ch[x][0];
    return x;
}
void link(int x,int y)
{
    reverse(x);
    f[x]=y;
}
void cut(int x,int y)
{
    reverse(x);
    access(y);
    splay(y);
    ch[y][0]=f[x]=0;
}
//--------------------------operation------------------------------
void add(int i,int cc)
{
    int x=e[i].x,y=e[i].y,d=e[i].d,id=e[i].id;
    if (find(x)==find(y))
    {
        reverse(x);
        access(y);
        splay(y);
        int loc=minn[y];
        if (d<=val[loc]) return;
        cut(loc,l[loc]);
        cut(loc,r[loc]);
        tree[re[loc]]=0;
    }
    else --ans[cc];
    ++sz;val[sz]=d;
    re[sz]=id;pt[id]=sz;tree[id]=1;
    l[sz]=x,r[sz]=y;
    link(x,sz);
    link(y,sz);
}
void del(int i)
{
    int x=e[i].x,y=e[i].y,id=e[i].id;
    cut(x,pt[id]);
    cut(y,pt[id]);
    tree[id]=0;
}
 
//---------------------------main----------------------------------
int main()
{
    n=read();
    for (int i=1;i<=n;++i)
        for (int j=1;j<=n;++j)
        {
            color[i][j]=c[i][j]=read();
            ++ans[c[i][j]];
        }
    for (int i=1;i<=n;++i)
        for (int j=1;j<=n;++j)
        {
            int num=(i-1)*n+j,cc=color[i][j];
            if (j!=n&&color[i][j+1]==cc)    
                e[++cnt].x=num,e[cnt].y=num+1,e[cnt].t=0,e[cnt].id=eid(i,j,i,j+1),e[cnt].opt=1,e[cnt].c=cc;
            if (i!=n&&color[i+1][j]==cc)
                e[++cnt].x=num,e[cnt].y=num+n,e[cnt].t=0,e[cnt].id=eid(i,j,i+1,j),e[cnt].opt=1,e[cnt].c=cc;
        }
 
    m=read();
    for (int i=1;i<=m;++i)
    {
        int x=read(),y=read();
        opr[i].x=x,opr[i].y=y;
        int num=(x-1)*n+y;
        for (int j=0;j<4;++j)
        {
            int nx=x+dx[j],ny=y+dy[j],nnum=(nx-1)*n+ny;
            if (nx<=0||ny<=0||nx>n||ny>n) continue;
            if (c[x][y]==c[nx][ny])
                e[++cnt].x=num,e[cnt].y=nnum,e[cnt].t=i,e[cnt].id=eid(x,y,nx,ny),e[cnt].opt=-1;
            else
                e[++cnt].x=num,e[cnt].y=nnum,e[cnt].t=i,e[cnt].id=eid(x,y,nx,ny),e[cnt].opt=1;
        }
        c[x][y]^=1;
    }
 
    sort(e+1,e+cnt+1,cmptopt);
    for (int i=1;i<=cnt;++i) e[i].d=m+1;
    mp.clear();
    for (int i=cnt;i>=1;--i)
    {
        if (e[i].x>e[i].y) swap(e[i].x,e[i].y);
        if (mp[e[i]]) e[i].d=mp[e[i]];
        mp[e[i]]=e[i].t;
    }
    sz=n*n;
    memset(val,127,sizeof(val));
    int now=1;
    for (;now<=cnt&&e[now].t<=0;++now)
        add(now,e[now].c);
    for (int i=1;i<=m;++i)
    {
        int x=opr[i].x,y=opr[i].y;
        int cc=color[x][y];
        for (;now<=cnt&&e[now].t<=i;++now)
            if (e[now].opt==-1)
            {
                if (!tree[e[now].id]) continue;
                del(now);
                ++ans[cc];
            }
            else add(now,cc^1);
        --ans[cc];
        ++ans[cc^1];
        color[x][y]^=1;
        printf("%d %d
",ans[1],ans[0]);
    }
}
LCT
原文地址:https://www.cnblogs.com/Kong-Ruo/p/8416280.html