题目连接:Codeforces - 811E - Vladik and Entertaining Flags
线段树结点
[L,R]
维护区间
[L,R]
的答案和第
L
列与第
两个相邻区间
[L1,R1]
和
[L2,R2]
合并时,第
R1
列与第
L2
列相邻的结点颜色相同的用并查集连接起来,同时答案-1就行。然后更新新结点的连通关系。
这样相当于线段树维护区间答案和区间并查集。
合并的复杂度是 O(n) ,因此总的复杂度为 O(nmlogm+nqlogm)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
struct Node
{
int l,r,ans,u[11],d[11];
}tr[N<<2];
int n,m,q,G[11][N];
int fa[41],vis[41];
int f(int x){return x==fa[x]?x:fa[x]=f(fa[x]); }
inline void Union(int x,int y)
{
x=f(x);y=f(y);
fa[x]=y;
}
Node merge(const Node& a,const Node& b)
{
Node res;
res.l=a.l;
res.r=b.r;
res.ans=a.ans+b.ans;
for(int i=1;i<=n;i++) fa[i]=a.u[i];
for(int i=1;i<=n;i++) fa[i+n]=a.d[i];
for(int i=1;i<=n;i++) fa[i+n*2]=b.u[i]+n*2;
for(int i=1;i<=n;i++) fa[i+n*3]=b.d[i]+n*2;
for(int i=1;i<=n;i++)
if(G[i][a.r]==G[i][b.l]&&f(i+n)!=f(i+n*2)) Union(i+n,i+2*n),--res.ans;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(!vis[f(i)]) res.u[i]=i,vis[f(i)]=i;
else res.u[i]=vis[f(i)];
if(!vis[f(i+3*n)]) res.d[i]=i+n,vis[f(i+3*n)]=i+n;
else res.d[i]=vis[f(i+3*n)];
}
return res;
}
void build(int rt,int l,int r)
{
if(l==r)
{
Node &t=tr[rt];
t.l=t.r=l;
t.u[1]=t.d[1]=t.ans=1;
for(int i=2;i<=n;i++)
if(G[i][l]!=G[i-1][l])
t.u[i]=t.d[i]=i,++t.ans;
else t.u[i]=t.d[i]=t.u[i-1];
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tr[rt]=merge(tr[rt<<1],tr[rt<<1|1]);
//printf("%d\n",tr[rt].ans);
}
Node query(int rt, int l, int r, int ql, int qr)
{
if(l>=ql&&r<=qr)
return tr[rt];
int mid=(l+r)>>1;
if(ql>mid) return query(rt<<1|1,mid+1,r,ql,qr);
else if(qr<=mid) return query(rt<<1,l,mid,ql,qr);
else return merge(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}
int main()
{
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]);
int l,r;
build(1,1,m);
while(q--)
{
scanf("%d%d",&l,&r);
printf("%d\n",query(1,1,m,l,r).ans);
}
return 0;
}