题目连接:Codeforces - 811E - Vladik and Entertaining Flags
线段树结点[L,R]维护区间 [L,R]的答案和第 L 列与第 R 列中所有结点的连通关系。
两个相邻区间 [L1,R1] 和 [L2,R2]合并时,第 R1 列与第 L2 列相邻的结点颜色相同的用并查集连接起来,同时答案-1就行。然后更新新结点的连通关系。
这样相当于线段树维护区间答案和区间并查集。
合并的复杂度是 O(n) ,因此总的复杂度为 O(nmlogm+nqlogm)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
int n,m,q;
struct node
{
int l,r;
int sum;
int ls[12],rs[12];
}tr[100005*5];
int G[12][100005];
int fa[55];
int vis[55];
int gf(int x)
{
if(x!=fa[x])
fa[x]=gf(fa[x]);
return fa[x];
}
void Union(int x,int y)
{
x=gf(x);y=gf(y);
fa[x]=y;
}
node up(node a,node b)
{
node res;
res.l=a.l;
res.r=b.r;
res.sum=a.sum+b.sum;
for(int i=1;i<=n;i++) fa[i]=a.ls[i];
for(int i=1;i<=n;i++) fa[i+n]=a.rs[i];
for(int i=1;i<=n;i++) fa[i+n*2]=b.ls[i]+n*2;
for(int i=1;i<=n;i++) fa[i+n*3]=b.rs[i]+n*2;
for(int i=1;i<=n;i++)
if(G[i][a.r]==G[i][b.l] && gf(i+n)!=gf(i+n*2)) Union(i+n,i+2*n),res.sum--;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(!vis[gf(i)]) res.ls[i]=i,vis[gf(i)]=i;
else res.ls[i]=vis[gf(i)];
if(!vis[gf(i+3*n)]) res.rs[i]=i+n,vis[gf(i+3*n)]=i+n;
else res.rs[i]=vis[gf(i+3*n)];
}
return res;
}
void build(int i,int l,int r)
{
if(l==r)
{
tr[i].l=l;
tr[i].r=r;
tr[i].ls[1]=1;
tr[i].rs[1]=1;
tr[i].sum=1;
for(int j=2;j<=n;j++)
if(G[j][l]!=G[j-1][l])
tr[i].ls[j]=tr[i].rs[j]=j,tr[i].sum++;
else
tr[i].ls[j]=tr[i].rs[j]=tr[i].ls[j-1];
return ;
}
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
tr[i]=up(tr[i*2],tr[i*2+1]);
}
node query(int i,int l,int r,int ql,int qr)
{
if(tr[i].l==ql && tr[i].r==qr)
{
return tr[i];
}
int mid=(l+r)/2;
if(ql>mid)
return query(i*2+1,mid+1,r,ql,qr);
else if(qr<=mid)
return query(i*2,l,mid,ql,qr);
else
return up(query(i*2,l,mid,ql,mid),query(i*2+1,mid+1,r,mid+1,qr));
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&G[i][j]);
}
}
build(1,1,m);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(1,1,m,l,r).sum);
}
}
}