[SCOI2016]萌萌哒【并查集】【倍增】

>Link

luogu P3295


>Description

在这里插入图片描述
n , m ≤ 1 0 5 n,m\le10^5 n,m105


>解题思路

我感觉这道题的操作跟 这道题 好像

一开始的想法:两个区间完全相同,说明两个区间每个对应的数字相同,可以搞一个并查集将这些数字并起来,最后并查集的数量就是可以不同的数的个数,答案用快速幂计算一下
但是这样的时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn),显然不行

正解就是倍增优化并查集的过程
正常的并查集为 f a i fa_i fai 表示 i i i 所属的集,这里用倍增, f a g , i fa_{g,i} fag,i 表示区间 [ i , i + 2 g − 1 ] [i,i+2^g-1] [i,i+2g1] 所属的集(把一个点分成 l o g n logn logn 个点)
每次操作,我们就把 [ l 1 , r 1 ] , [ l 2 , r 2 ] [l_1,r_1],[l_2,r_2] [l1,r1],[l2,r2] 区间都分成两个 2 2 2 的次幂的区间,把对应相同的区间并在一起,最后我们再从大到小把区间间的关系往下传。下传操作也是一样的,原区间分成两个区间,然后把对应相同的部分并在一起
最后并查集的数量就是 f a 0 , i fa_{0,i} fa0,i 的数量


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define N 100010
using namespace std;

const LL Mod = 1e9 + 7;
int n, m, fa[20][N], lg[N], sum;

int find (int g, int now)
{
	if (fa[g][now] == now) return now;
	return fa[g][now] = find (g, fa[g][now]);
}
void connect (int g, int x, int y)
{
	if (x == y) return;
	int fx = find (g, x), fy = find (g, y);
	if (fx != fy) fa[g][fx] = fy;
}
void pushdown (int g, int x)
{
	int fx = find (g, x);
	connect (g - 1, x, fx);
	connect (g - 1, x + (1 << (g - 1)), fx + (1 << (g - 1)));
}
LL power (LL aa, LL bb)
{
	LL ret = 1;
	for (aa %= Mod; bb; bb >>= 1, aa = aa * aa % Mod)
	  if (bb & 1) ret = ret * aa % Mod;
	return ret;
}

int main()
{
	int g, l, r, ll, rr;
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
	  lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	for (int i = 1; i <= n; i++) lg[i]--;
	for (int i = 0; i <= lg[n]; i++)
	  for (int j = 1; j <= n; j++)
	    fa[i][j] = j;
	for (int i = 1; i <= m; i++)
	{
		scanf ("%d%d%d%d", &l, &r, &ll, &rr);
		g = lg[r - l + 1];
		connect (g, l, ll);
		connect (g, r - (1 << g) + 1, rr - (1 << g) + 1);
	}
	for (int i = lg[n]; i >= 1; i--)
	  for (int j = 1; j <= n - (1 << i) + 1; j++)
	    pushdown (i, j);
	for (int i = 1; i <= n; i++)
	  if (find (0, i) == i) sum++;
	sum--;
	printf ("%lld", (LL)9 * power (10, sum) % Mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值