题目描述
传送门
题目大意:用公共边且颜色相同的两个块属于同一个连通块,每次询问一个区间中有多少个连通块块。
题解
线段树+并查集
比较基本的思路就是因为行数比较小所以可以对于每个区间的左右两列维护并查集。
维护的东西必须保证在该区间中可以连通的两个位置的编号相同。
两个区间合并的时候,将两个区间左右两列的编号在并查集中的父亲赋值成自己。如果两个区间相邻的位置颜色相同就用并查集合并起来,然后此时再从并查集中查询区间左右列的编号即可。
也就是说我们每个区间的左右两列维护的是一个连通块的编号,合并的时候用并查集进行合并。
具体过程见代码吧。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100003
using namespace std;
int L[N*4][12],R[N*4][12],sum[N*4],cnt,q;
int fa[N*10],a[12][N],n,m,ls[13],rs[13],ans;
bool pd=false;
int find(int x)
{
if (fa[x]==x) return fa[x];
fa[x]=find(fa[x]);
return fa[x];
}
void update(int now,int l,int r)
{
int mid=(l+r)/2;
int ls=now<<1; int rs=now<<1|1;
sum[now]=sum[ls]+sum[rs];
for (int i=1;i<=n;i++) {
fa[L[ls][i]]=L[ls][i];
fa[L[rs][i]]=L[rs][i];
fa[R[ls][i]]=R[ls][i];
fa[R[rs][i]]=R[rs][i];
}
for (int i=1;i<=n;i++)
if (a[i][mid]==a[i][mid+1]) {
int r1=find(L[rs][i]); int r2=find(R[ls][i]);
if (r1!=r2) {
sum[now]--;
fa[r2]=r1;
}
}
for (int i=1;i<=n;i++) {
L[now][i]=find(L[ls][i]);
R[now][i]=find(R[rs][i]);
}
}
void build(int now,int l,int r)
{
if (l==r) {
for (int i=1;i<=n;i++)
if (a[i][l]==a[i-1][l]) L[now][i]=R[now][i]=L[now][i-1];
else L[now][i]=R[now][i]=++cnt,sum[now]++;
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now,l,r);
}
void query(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) {
if (!pd) {
pd=true;
ans=sum[now];
for (int i=1;i<=n;i++) ls[i]=L[now][i],rs[i]=R[now][i];
}
else {
ans+=sum[now];
for (int i=1;i<=n;i++) {
fa[ls[i]]=ls[i];
fa[L[now][i]]=L[now][i];
fa[rs[i]]=rs[i];
fa[R[now][i]]=R[now][i];
}
for (int i=1;i<=n;i++)
if (a[i][l]==a[i][l-1]) {
int r1=find(L[now][i]); int r2=find(rs[i]);
if (r1!=r2) {
ans--;
fa[r2]=r1;
}
}
for (int i=1;i<=n;i++) {
ls[i]=find(ls[i]);
rs[i]=find(R[now][i]);
}
}
return;
}
int mid=(l+r)/2;
if (ll<=mid) query(now<<1,l,mid,ll,rr);
if (rr>mid) query(now<<1|1,mid+1,r,ll,rr);
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) scanf("%d",&a[i][j]);
build(1,1,m);
for (int i=1;i<=q;i++) {
int l,r; scanf("%d%d",&l,&r);
pd=false; ans=0;
query(1,1,m,l,r);
printf("%d\n",ans);
}
}