BZOJ 1453 Wc2005 Dface双面棋盘 Link-Cut-Tree

题目大意:给定一张网格图,每个点有黑色和白色,同色相邻的点之间连边,每次反转一个点的颜色并输出黑色和白色的联通块个数
《论科技不发达的危害》
这显然是个动态图问题,由于不强制在线,我们用Link-Cut-Tree维护边删除时间的最大生成树就可以了
时间复杂度 O((n2+m)logn)
常数巨大

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 40400
#define P(x,y) ((x)*n-n+(y))
using namespace std;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int n,m,ans[2];
int a[220][220];
queue<int> ed[M<<1];
pair<int,int> edges[M<<1];
pair<int,int> operations[10100];
namespace Link_Cut_Tree{
    bool Compare(int x,int y)
    {
        if(!x) return false;
        if(!y) return true;
        return ed[x].front()<ed[y].front();
    }
    struct abcd{
        abcd *ls,*rs,*fa;
        int e,min_e;
        bool rev_mark;
        abcd(int _);
        void Push_Up();
        void Push_Down();
        void Reverse();
    }*null=new abcd(0),*tree[M],*edge[M<<1];
    abcd :: abcd(int _)
    {
        ls=rs=fa=null;
        e=min_e=_;
        rev_mark=false;
    }
    void abcd :: Push_Up()
    {
        min_e=min(min(ls->min_e,rs->min_e,Compare),e,Compare);
    }
    void abcd :: Push_Down()
    {
        if(fa->ls==this||fa->rs==this)
            fa->Push_Down();
        if(rev_mark)
        {
            ls->Reverse();
            rs->Reverse();
            rev_mark=false;
        }
    }
    void abcd :: Reverse()
    {
        swap(ls,rs);
        rev_mark^=1;
    }
    void Zig(abcd *x)
    {
        abcd *y=x->fa;
        y->ls=x->rs;
        x->rs->fa=y;
        x->rs=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
        y->Push_Up();
    }
    void Zag(abcd *x)
    {
        abcd *y=x->fa;
        y->rs=x->ls;
        x->ls->fa=y;
        x->ls=y;
        x->fa=y->fa;
        if(y==y->fa->ls)
            y->fa->ls=x;
        else if(y==y->fa->rs)
            y->fa->rs=x;
        y->fa=x;
        y->Push_Up();
    }
    void Splay(abcd *x)
    {
        x->Push_Down();
        while(x->fa->ls==x||x->fa->rs==x)
        {
            abcd *y=x->fa,*z=y->fa;
            if(x==y->ls)
            {
                if(y==z->ls)
                    Zig(y);
                Zig(x);
            }
            else
            {
                if(y==z->rs)
                    Zag(y);
                Zag(x);
            }
        }
        x->Push_Up();
    }
    void Access(abcd *x)
    {
        abcd *y=null;
        while(x!=null)
        {
            Splay(x);
            x->rs=y;
            x->Push_Up();
            y=x;x=x->fa;
        }
    }
    void Move_To_Root(abcd *x)
    {
        Access(x);
        Splay(x);
        x->Reverse();
    }
    abcd* Find_Root(abcd *x)
    {
        while(x->fa!=null)
            x=x->fa;
        return x;
    }
    bool Connect(abcd *x,abcd *y)
    {
        Access(x);
        Splay(x);
        bool re=Find_Root(y)==x;
        Access(y);
        return re;
    }
    void Link(abcd *x,abcd *y)
    {
        Move_To_Root(x);
        x->fa=y;
    }
    bool Cut(abcd *x,abcd *y)
    {
        Move_To_Root(x);
        Access(y);
        Splay(y);
        if( y->ls!=x || x->rs!=null )
            return false;
        y->ls=null;
        x->fa=null;
        y->Push_Up();
        return true;
    }
}

void Cut(int e,bool col)
{
    using namespace Link_Cut_Tree;
    int x=edges[e].first;
    int y=edges[e].second;
    if( Cut(edge[e],tree[x]) && Cut(edge[e],tree[y]) )
        ++ans[col];
    ed[e].pop();
}

void Link(int e,bool col)
{
    using namespace Link_Cut_Tree;
    int x=edges[e].first;
    int y=edges[e].second;
    if(!Connect(tree[x],tree[y]) )
    {
        Link(edge[e],tree[x]);
        Link(edge[e],tree[y]);
        --ans[col];
        return ;
    }
    Move_To_Root(tree[x]);
    Access(tree[y]);
    Splay(tree[y]);
    int _e=tree[y]->min_e;
    if( Compare(_e,e) )
    {
        int _x=edges[_e].first;
        int _y=edges[_e].second;
        Cut(edge[_e],tree[_x]);
        Cut(edge[_e],tree[_y]);
        Link(edge[e],tree[x]);
        Link(edge[e],tree[y]);
    }
}

int main()
{
    using namespace Link_Cut_Tree;
    int i,j,k,x,y;

    cin>>n;

    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    //读入

    for(i=1;i<=n;i++)
        for(j=1;j<n;j++)
            edges[P(i,j)]=pair<int,int>(P(i,j),P(i,j+1));
    for(i=1;i<n;i++)
        for(j=1;j<=n;j++)
            edges[P(i,j)+n*n]=pair<int,int>(P(i,j),P(i+1,j));
    //设定每条边对应的两端点

    cin>>m;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        operations[i].first=x;
        operations[i].second=y;
        for(k=0;k<4;k++)
        {
            int xx=x+dx[k];
            int yy=y+dy[k];
            if(!xx||!yy||xx==n+1||yy==n+1)
                continue;
            if(a[x][y]==a[xx][yy])
                ed[min(P(x,y),P(xx,yy))+(k>>1)*n*n].push(i);
        }
        a[x][y]^=1;
    }
    //读入数据&&记录每条边的终止时刻

    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            for(k=0;k<4;k+=2)
            {
                int xx=i+dx[k];
                int yy=j+dy[k];
                if(!xx||!yy||xx==n+1||yy==n+1)
                    continue;
                if(a[i][j]==a[xx][yy])
                    ed[P(i,j)+(k>>1)*n*n].push(m+1);
            }
    //记录最后没有被删除的边的终止时刻

    for(i=1;i<=n*n;i++)
        tree[i]=new abcd(0);
    for(i=1;i<=n;i++)
        for(j=1;j<n;j++)
            edge[P(i,j)]=new abcd(P(i,j));
    for(i=1;i<n;i++)
        for(j=1;j<=n;j++)
            edge[P(i,j)+n*n]=new abcd(P(i,j)+n*n);
    //申请内存

    for(i=m;i;i--)
    {
        x=operations[i].first;
        y=operations[i].second;
        a[x][y]^=1;
    }
    //还原a数组

    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            ans[a[i][j]]++;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            for(k=0;k<4;k+=2)
            {
                int xx=i+dx[k];
                int yy=j+dy[k];
                if(!xx||!yy||xx==n+1||yy==n+1)
                    continue;
                if(a[i][j]==a[xx][yy])
                    Link(P(i,j)+(k>>1)*n*n,a[i][j]);
            }
    //统计初始答案&&连接初始的边

    for(i=1;i<=m;i++)
    {
        x=operations[i].first;
        y=operations[i].second;
        for(k=0;k<4;k++)
        {
            int xx=x+dx[k];
            int yy=y+dy[k];
            if(!xx||!yy||xx==n+1||yy==n+1)
                continue;
            if(a[x][y]==a[xx][yy])
                Cut(min(P(x,y),P(xx,yy))+(k>>1)*n*n,a[x][y]);
        }
        ans[a[x][y]]--;
        a[x][y]^=1;
        ans[a[x][y]]++;
        for(k=0;k<4;k++)
        {
            int xx=x+dx[k];
            int yy=y+dy[k];
            if(!xx||!yy||xx==n+1||yy==n+1)
                continue;
            if(a[x][y]==a[xx][yy])
                Link(min(P(x,y),P(xx,yy))+(k>>1)*n*n,a[x][y]);
        }
        printf("%d %d\n",ans[1],ans[0]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值