http://codeforces.com/problemset/problem/811/E
cf的题面真的是做的美美的,
所以就直接扔超链接了
分析:
线段树的叶子结点维护一列的信息
包括左右端点,有多少联通块,以及左右两列的并查集情况
需要注意的是,
这次代码中的update返回的是一个node
第一次代码re的原因就是在返回的这个新建的node中
我没有维护左右端点
tip
在update维护左右两列的并查集时,
(160个点,怀疑人生。。。)
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=1e5+7;
struct node{
int x,y,l[11],r[11],sum;
};
node tree[N<<2];
int n,m,q,mp[11][N];
int fa[N*11],tot=0;
int find(int a)
{
if (fa[a]!=a) fa[a]=find(fa[a]);
return fa[a];
}
node update(int ll,int rr,node lc,node rc,int mid)
{
int i;
node ans;
ans.x=ll;
ans.y=rr;
ans.sum=lc.sum+rc.sum;
for (i=1;i<=n;i++)
{
fa[lc.l[i]]=lc.l[i];
fa[lc.r[i]]=lc.r[i];
fa[rc.l[i]]=rc.l[i];
fa[rc.r[i]]=rc.r[i];
}
for (i=1;i<=n;i++)
{
if (mp[i][mid]==mp[i][mid+1])
{
int f1=find(lc.r[i]);
int f2=find(rc.l[i]);
if (f1!=f2)
{
fa[f1]=f2;
ans.sum--;
}
}
}
for (i=1;i<=n;i++)
{
ans.l[i]=find(lc.l[i]); ///
ans.r[i]=find(rc.r[i]); ///
}
return ans;
}
void build(int bh,int ll,int rr)
{
tree[bh].x=ll;
tree[bh].y=rr;
if (ll==rr)
{
tree[bh].sum=n;
for (int i=1;i<=n;i++)
{
if (mp[i][ll]==mp[i-1][ll])
tree[bh].l[i]=tree[bh].r[i]=tree[bh].l[i-1],tree[bh].sum--;
else
tree[bh].l[i]=tree[bh].r[i]=++tot;
}
return;
}
int mid=(ll+rr)>>1;
build(bh<<1,ll,mid);
build(bh<<1|1,mid+1,rr);
tree[bh]=update(tree[bh].x,tree[bh].y,tree[bh<<1],tree[bh<<1|1],mid);
}
node ask(int bh,int ll,int rr)
{
if (tree[bh].x>=ll&&tree[bh].y<=rr)
{
return tree[bh];
}
int mid=(tree[bh].x+tree[bh].y)>>1;
if (rr<=mid) return ask(bh<<1,ll,rr);
else if (ll>mid) return ask(bh<<1|1,ll,rr);
else
{
return update(ll,rr,ask(bh<<1,ll,rr),ask(bh<<1|1,ll,rr),mid);
}
}
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",&mp[i][j]);
tot=0;
build(1,1,m);
while (q--)
{
int u,w;
scanf("%d%d",&u,&w);
node ans=ask(1,u,w);
printf("%d
",ans.sum);
}
return 0;
}