Codeforces 811E Vladik and Entertaining Flags[线段树][并查集]

题意:给了一个n*m的矩阵,上下左右相同数的点可以并为同一个联通块,对于每个询问L,R 输出(1,L) ... (n,R)的块中有几个联通块。


分析:用线段树维护每列的状况,一个sum表示这一块的联通块个数,ls[i],rs[i]表示这块中第i行左边的标号,右边的标号,标号用来表示属于哪一个联通块。

每次pushup,先是sum相加,一行行判断是否能合并成一个联通块,如果左右数值相同,而且,左右标号不同的话,sum--,

左右标号用并查集合并。


具体看以下代码。

struct lx {
	int rs[11], ls[11], sum;
}tree[MAXN * 4];
int n;
int mp[11][MAXN], tot;
int fa[MAXN * 10];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void mix(int x, int y) {
	int fx = find(x), fy = find(y);
	if (fx != fy) {
		fa[fx] = fy;
	}
}
lx pushup(lx a, lx b,int mid) {
	lx res;
	res.sum = a.sum + b.sum;
	for (int i = 1; i <= n; ++i) {
		fa[a.ls[i]] = a.ls[i];
		fa[b.ls[i]] = b.ls[i];
		fa[a.rs[i]] = a.rs[i];
		fa[b.rs[i]] = b.rs[i];
	}
	for (int i = 1; i <= n; ++i)if (mp[i][mid] == mp[i][mid + 1]) {
		if (find(a.rs[i]) != find(b.ls[i])) {
			res.sum--;
			mix(a.rs[i], b.ls[i]);
		}
	}
	for (int i = 1; i <= n; ++i) {
		res.ls[i] = find(a.ls[i]);
		res.rs[i] = find(b.rs[i]);
	}
	return res;
}
void build(int l, int r, int id) {
	if (l == r) {
		for (int i = 1; i <= n; ++i) {
			if (mp[i - 1][l] == mp[i][l])tree[id].ls[i] = tree[id].rs[i] = tree[id].ls[i - 1];
			else tree[id].ls[i] = tree[id].rs[i] = ++tot, tree[id].sum++;
		}
		return;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	tree[id] = pushup(tree[id << 1], tree[id << 1 | 1], mid);
}
lx query(int l, int r, int id, int L, int R) {
	if (L <= l&&r <= R) {
		return tree[id];
	}
	int mid = (l + r) >> 1;
	if (R <= mid)return query(lson, L, R);
	else if (L > mid)return query(rson, L, R);
	else
	{
		return pushup(query(lson, L, R), query(rson, L, R), mid);
	}
}
int main() {
	memset(tree, 0, sizeof(tree));
	tot = 0;
	int m, q, x, y;
	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]);
		}
	}
	build(1, m, 1);
	while (q--) {
		scanf("%d%d", &x, &y);
		lx ans = query(1, m, 1, x, y);
		printf("%d\n", ans.sum);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值