【LG3295】[SCOI2016]萌萌哒

题面

洛谷

题解

考虑现在我们如果一次只是限定两个位置相等该怎么做,

直接将这些位置用并查集并起来然后答案就是

\[ans=
\begin{cases}
10 & n=1\\
9\times 10^{t-1} & \text{otherwise}
\end{cases}
\]

其中\(t\)为联通块的个数。

现在我们是给定两个区间,我们将这两个区间中的数两两并起来算,复杂度\(O(n^2)\)。

考虑优化上面的过程:

维护\(\log n\)个并查集,第\(i\)个并查集维护的是区间长度为\(2^i\)的区间相等的情况。

那么我们每次只要合并两个并查集就行了。

高层的并查集显然对下面的无影响,我们到最后将下层合并到上层,最后统计底层并查集联通块个数即可。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int Mod = 1e9 + 7;
const int MAX_N = 1e5 + 5;
int N, M, lg[MAX_N], bin[20];
int f[20][MAX_N];
int getf(int *p, int x) { return p[x] == x ? x : p[x] = getf(p, p[x]); }
void merge(int len, int x, int y) {
x = getf(f[len], x), y = getf(f[len], y);
if (x != y) f[len][x] = y;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
N = gi(), M = gi();
if (N == 1) return puts("9") & 0;
bin[0] = 1; for (int i = 1; i < 20; i++) bin[i] = bin[i - 1] << 1;
for (int i = 2; i <= N; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 0; bin[i] <= N; i++)
for (int j = 1; j <= N; j++) f[i][j] = j;
while (M--) {
int l1 = gi(), r1 = gi(), l2 = gi(), r2 = gi();
int len = lg[r1 - l1 + 1];
merge(len, l1, l2);
merge(len, r1 - bin[len] + 1, r2 - bin[len] + 1);
}
for (int i = lg[N]; i; i--)
for (int j = 1; j + bin[i] - 1 <= N; j++) {
merge(i - 1, j, getf(f[i], j));
merge(i - 1, j + bin[i - 1], getf(f[i], j) + bin[i - 1]);
}
int ans = 9, cnt = 0;
for (int i = 1; i <= N; i++) if (getf(f[0], i) == i) ++cnt;
for (int i = 1; i < cnt; i++) ans = 10ll * ans % Mod;
printf("%d\n", ans);
return 0;
}
05-11 14:00