题目大意:给定一张网格图,每个点有黑色和白色,同色相邻的点之间连边,每次反转一个点的颜色并输出黑色和白色的联通块个数
《论科技不发达的危害》
这显然是个动态图问题,由于不强制在线,我们用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;
}