您的位置:首页 > 大数据 > 人工智能

codeforces 811 E Vladik and Entertaining Flags(线段树+并查集)

2017-05-31 20:54 525 查看
题意:

n*m的矩形,每个格子上有一个数字代表颜色,有q次询问,问(1,l),(n,r)这两个点对应的矩形的连通块有多少个。

1<n<10, 1<m<100000

解题思路 :

这题最麻烦的就是写合并了,然后也是让我对并查集又重新认识了一下。。

只要往线段树那个方向想了,想到每个区间维护左右两个列应该不难,然后再想一想这个题目要求连通块个数,那么应该也能想到左右两个列应该是并查集。

然后需要注意的是L(左边的列)数组和R数组不是单独的列上的并查集,而是从l到r上的整个区间的并查集的一部分,有什么区别呢?

如图



第二列里(1,2)和(3,2)颜色相同,如果是单独的列的话,他们不属于同一个祖先,如果是[1,2]整个区间上的并查集的话,(1,2)和(3,2)是属于同一个祖先的,这两者是有区别的。

合并的时候就是cnt=lson.cnt+rson.cnt,然后两个区间中间两列的并查集每合并一次,cnt--,最后cnt就是这个区间的连通数量了。

然后上面提到的那个区别为什么呢?

假如(2,4)和(3,4)也是1的话,区间[1,2]和区间[3,4]合并的时候如果2,3列的并查集只是单独自己列上的并查集的话,cnt需要减2次,而实际上应该只减1次,所以应该维护的是整个区间的并查集。

然后学到了把并查集下标和值同时+2*n这种操作。

需要注意的是并查集数组里的值也就是那几个祖先的范围和下标的范围也该是一样的,合并操作最后得到的两个并查集需要重新赋值。(看代码就知道了)

代码:

#include <bits/stdc++.h>
#define lson o<<1
#define rson o<<1|1
#define ps push_back
using namespace std;
const int maxn=1e5+5;
int n, m, q;
int a[11][maxn];

struct p
{
int l, r;
int c[22];
int x;
void init()
{
x=0;

return;
}
}val[maxn<<2];
vector<int>tmp[11*4];
int f[44];
int getf(int x)
{
if(x==f[x])return x;
else return f[x]=getf(f[x]);
}
int merg(int x, int y)
{
int a=getf(x), b=getf(y);
if(a>b)swap(a,b);
if(a!=b)
{
f[b]=a;
return 1;
}
else return 0;
}

void init(int n)
{
for(int i=1; i<=n; i++)f[i]=i;
return;
}

p operator + (const  p &A, const  p &B)
{
p res;
res.l=A.l, res.r=B.r;
res.x=A.x+B.x;
for(int i=1; i<=2*n; i++)
{
f[i]=A.c[i];
f[i+2*n]=B.c[i]+2*n;
}
for(int i=1; i<=n; i++)
{
if(a[i][A.r]==a[i][B.l])
{
res.x-=merg(i+2*n, i+n);
}
}

for(int i=1; i<=4*n; i++)tmp[i].clear();
for(int i=1; i<=n; i++)
{
tmp[getf(i)].ps(i);
tmp[getf(i+3*n)].ps(i+n);
}
for(int i=1; i<=4*n; i++)
{
for(int j=0; j<(int)tmp[i].size(); j++)
{
res.c[tmp[i][j]]=tmp[i][0]; //重新赋值,使祖先的范围和下标的范围对应
}
}

return res;
}

void build(int o, int l, int r)
{
val[o].l=l, val[o].r=r;
if(l==r)
{
val[o].x=n;
init(n);
for(int i=2; i<=n; i++)
{
if(a[i][l]==a[i-1][l])
{
val[o].x-=merg(i, i-1);
}
}

for(int i=1; i<=n; i++)
{
val[o].c[i]=val[o].c[i+n]=getf(i);
}

return;
}
int mid=(l+r)>>1;

build(lson, l, mid);
build(rson, mid+1, r);
val[o]=val[lson]+val[rson];

return;

}

p query(int o, int l, int r, int ll, int rr)
{
if(l==ll && r==rr)
{
return val[o];
}
int mid=(l+r)>>1;
if(rr<=mid)return query(lson, l, mid, ll, rr);

if(ll>mid)return query(rson, mid+1, r, ll, rr);

return query(lson, l, mid, ll, mid)+query(rson, mid+1, r, mid+1, rr);
}

int main()
{
scanf("%d%d%d", &n, &m, &q);
int i, j, k, x, y;
for(i=1; i<=n; i++)
{
for(j=1; j<=m; j++)
{
scanf("%d", &a[i][j]);
}
}

build(1, 1, m);

int l, r;
while(q--)
{
scanf("%d%d", &l, &r);
printf("%d\n", query(1, 1, m, l, r).x);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: