Luogu P3295 【[SCOI2016]萌萌哒】

Description

传送门


Solution

首先先考虑如果限制不是区间,而是告诉你两个位置的字符相等的做法。

这样的话,我们可以把相等的位置加进一些集合里,最后答案就是\(9 \times 10 ^ {num - 1}\),其中\(num\)代表不同的集合个数,这个可以简单地用并查集维护连通性。

那么如果是两个区间的话,考虑最暴力的做法,直接把区间里对应的点加进一个集合然后按单点跑,这样肯定是过不了的,考虑优化。

首先发现区间相等这个性质满足结合律,即

\[Merge(l1, r1, l2, r2) = Merge(Merge(l3, r3, l4, r4), Merge(l5, r5, l6, r6)) \]

其中满足

\[[l3, r3] \cup [l4, r4] = [l1, r1] \]

\[[l5, r5] \cup [l6, r6] = [l2, r2] \]

这样的话我们就不用一个一个枚举区间里的位置加进并查集,而是可以像\(st\)表一样将每两个区间拆成四个\(2\)的幂次的形式的区间的并,将这四个区间分别加入两个并查集。

所有的区间都拆完之后我们发现现在维护连通性的都是\(2\)的幂长度的区间,而我们需要的是长度为\(1\)的区间的连通信息。这样我们可以考虑将区间下放,将每个区间拆成两个等长的区间,发现对于一个并查集里的任意一个区间,只把它和这个集合的代表元素拆开就行。

所以每一层最多下放\(n\)个区间,一共有\(\log_n\)层,那么复杂度就是\(O(nlog_n)\)的。当然这里没有考虑并查集的复杂度。


Code

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 100000;
const int MOD = 1000000007;

int n, m, fa[N + 50][25], lg[N + 50], ans;

int Ksm(int a, int b)
{
	int tmp = 1;
	b = b % (MOD - 1);
	while (b)
	{
		if (b & 1) tmp = 1LL * tmp * a % MOD;
		a = 1LL * a * a % MOD;
		b >>= 1;
	}
	return tmp;
}

int Find(int x, int k)
{
	return fa[x][k] == x ? x : fa[x][k] = Find(fa[x][k], k);
}

void Merge(int l1, int l2, int k)
{
//	cout << l1 << " " << l2 << " " << k << endl;
	int fu = Find(l1, k), fv = Find(l2, k);
	if (fu == fv) return;
	fa[fu][k] = fv;
	return;
}

void Pushdown()
{
	for (int j = lg[n]; j >= 1; j--)
		for (int i = 1; i + (1 << j) <= n; i++)
		{
			int x = Find(i, j);
			if (x == i) continue;
	//		cout << i << " " << x << endl;
			Merge(i, x, j - 1);
			Merge(i + (1 << j - 1), x + (1 << j - 1), j - 1);
		}
	return;
}

int main()
{
	scanf("%d%d", &n, &m);
	lg[0] = -1;
	for (int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= lg[n]; j++)
			fa[i][j] = i;
	for (int i = 1, l1, l2, r1, r2; i <= m; i++)
	{
		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
		int length = r1 - l1 + 1;
		Merge(l1, l2, lg[length]);
		Merge(r1 - (1 << lg[length]) + 1, r2 - (1 << lg[length]) + 1, lg[length]); 
	}
	Pushdown();
	for (int i = 1; i <= n; i++) if (fa[i][0] == i) ans++;
	printf("%d", 9LL * Ksm(10, ans - 1) % MOD);
	return 0;
} 
posted @ 2020-06-12 09:52  Tian-Xing  阅读(81)  评论(0编辑  收藏  举报