Description
给出一 n×n n × n 的 01 01 矩阵,每行每列连通,且每行第 n n 个元素和第个元素也连通,两种操作,第一种是反转一列,第二种是反转一个元素,每次操作过后查询整个矩阵中 1 1 连通块数量和连通块数量
Input
第一行一整数 T T 表示用例组数,每组用例首先输入一整数,之后输入一个 n×n n × n 的 01 01 矩阵,之后输入一整数 q q 表示操作次数,最后行每行首先输入操作类型 type t y p e ,如果 type=1 t y p e = 1 则输入一整数 y y 表示反转第列,如果 type=2 t y p e = 2 则输入两整数 x,y x , y 表示反转第 x x 行第列的元素
(1≤T≤15,2≤n≤200,1≤q≤20000) ( 1 ≤ T ≤ 15 , 2 ≤ n ≤ 200 , 1 ≤ q ≤ 20000 )
Output
每次操作后输入整个矩阵中 1 1 连通块数量和连通块数量
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
Solution
以行建线段树,用并查集维护连通块,线段树一个节点所代表的区间 [l,r] [ l , r ] 维护第 l l 列和第列的状态(0/1)以及每个位置所处的连通块编号,同时维护该区间代表的这个子矩阵中的黑色连通块个数以及白色连通块个数,每次合并的时候暴力合并,最后合并根节点左右两边即为答案,注意由于修改操作,并查集每次要初始化,如果全部初始化时间复杂度 O(Tqn2) O ( T q n 2 ) 显然不行,故每次将并查集中被修改节点打标记存下来,初始化时只初始化被打标记的点
Code
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
namespace fastIO
{
#define BUF_SIZE 100000
//fread -> read
bool IOerror=0;
inline char nc()
{
static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
if(p1==pend)
{
p1=buf;
pend=buf+fread(buf,1,BUF_SIZE,stdin);
if(pend==p1)
{
IOerror=1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch)
{
return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
}
inline void read(int &x)
{
char ch;
while(blank(ch=nc()));
if(IOerror)return;
int sgn=1;
if(ch=='-')sgn=-1;
for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
x=sgn*x;
}
#undef BUF_SIZE
};
using namespace fastIO;
#define maxn 205
#define ls (t<<1)
#define rs ((t<<1)|1)
int W[maxn<<2],B[maxn<<2],Val[maxn<<2][2][maxn],Num[maxn<<2][2][maxn];
int T,n,q,fa[maxn*maxn],a[maxn][maxn],vis[maxn*maxn];
vector<int>v;
int ID(int x,int y)
{
return (x-1)*n+y;
}
int find(int x)
{
if(fa[x]==x)return x;
if(!vis[x])
{
vis[x]=1;
v.push_back(x);
}
return fa[x]=find(fa[x]);
}
void init(int t,int l)
{
W[t]=B[t]=0;
for(int i=1;i<=n;i++)Val[t][0][i]=Val[t][1][i]=a[i][l];
for(int i=1;i<=n;i++)
if(i==1||a[i][l]!=a[i-1][l])
{
Num[t][0][i]=Num[t][1][i]=ID(i,l);
if(a[i][l])B[t]++;
else W[t]++;
}
else Num[t][0][i]=Num[t][1][i]=Num[t][0][i-1];
}
void push_up(int t)
{
W[t]=W[ls]+W[rs];B[t]=B[ls]+B[rs];
for(int i=1;i<=n;i++)
if(Val[ls][1][i]==Val[rs][0][i])
{
int x=find(Num[ls][1][i]),y=find(Num[rs][0][i]);
if(x==y)continue;
if(!vis[x])
{
vis[x]=1;
v.push_back(x);
}
fa[x]=y;
if(Val[ls][1][i])B[t]--;
else W[t]--;
}
for(int i=1;i<=n;i++)
{
Val[t][0][i]=Val[ls][0][i];Val[t][1][i]=Val[rs][1][i];
Num[t][0][i]=find(Num[ls][0][i]);Num[t][1][i]=find(Num[rs][1][i]);
}
}
void build(int l,int r,int t)
{
if(l==r)
{
init(t,l);
return ;
}
int mid=(l+r)/2;
build(l,mid,ls);build(mid+1,r,rs);
push_up(t);
}
void update(int x,int l,int r,int t)
{
if(l==r)
{
init(t,l);
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(x,l,mid,ls);
else update(x,mid+1,r,rs);
push_up(t);
}
void Clear()
{
for(int i=0;i<v.size();i++)fa[v[i]]=v[i],vis[v[i]]=0;
v.clear();
}
int main()
{
read(T);
while(T--)
{
read(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
read(a[i][j]);
for(int i=1;i<=n*n;i++)fa[i]=i,vis[i]=0;
v.clear();
build(1,n,1);
Clear();
read(q);
while(q--)
{
int type,x,y;
read(type);
if(type==1)
{
read(y);
for(int i=1;i<=n;i++)a[i][y]^=1;
}
else
{
read(x);read(y);
a[x][y]^=1;
}
update(y,1,n,1);
Clear();
int w=W[1],b=B[1];
for(int i=1;i<=n;i++)
if(Val[1][1][i]==Val[1][0][i])
{
int x=find(Num[1][1][i]),y=find(Num[1][0][i]);
if(x==y)continue;
if(!vis[x])
{
vis[x]=1;
v.push_back(x);
}
fa[x]=y;
if(Val[1][1][i])b--;
else w--;
}
printf("%d %d\n",w,b);
Clear();
}
}
return 0;
}