萌萌哒
一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \cdots S_n\)表示,其中 \(S_i\) 表示数的第 \(i\) 位, \(S_1\) 是数的最高位。告诉你一些限制条件,每个条件表示为四个数,\(l_1,r_1,l_2,r_2\),即两个长度相同的区间,表示子串 \(S_{l_1}S_{l_1+1}S_{l_1+2} \cdots S_{r_1}\) 与 \(S_{l_2}S_{l_2+1}S_{l_2+2} \cdots S_{r_2}\) 完全相同。
比如 \(n=6\) 时,某限制条件 \(l_1=1,r_1=3,l_2=4,r_2=6\) ,那么 \(123123\),\(351351\) 均满足条件,但是 \(12012\),\(131141\) 不满足条件,前者数的长度不为 \(6\) ,后者第二位与第五位不同。问满足以上所有条件的数有多少个。
\(n\le 10^5\)。
首先我们有暴力做法,将限制条件两个区间的每个点合并,比如区间 \([1,2]\) 的 \(1\),和区间 \([3,4]\) 的 \(3\) 合并。这样做的原因是因为这两个点必须取同样的值。合并完之后,若连通块个数有 \(k\) 个,答案即为 \(10^{k-1} \times 9\)。这样时间复杂度是 \(O(nm\log n)\) 的,无法通过。
我们有一种好的做法是将整个序列倍增,这样我们一共就有 \(O(n\log n)\) 区间。然后对于一个操作,我们可以整体考虑,将这个各两个区间分成两个倍增区间。然后在分别合并四个区间。这样我们就会得到每个起点为 \(i\),长度为 \(2^{k}\) 的区间的所属集合。然后我们从上往下传递所属集合,比如 \([1,4] \in [5,8]\),传递变成 \([1,2] \in [5,6],[3,4] \in [7,8]\)。注意我们一层最多传递 \(O(n)\) 次,因为我们有并查集。最后在 \(k=1\) 一层统计连通块个数即可。时间复杂度 \(O(n\log^2 n)\)。
本质上,我们在上层就复合了信息,再传递到下层,减少了普通下层的操作次数。
// Problem: P3295 [SCOI2016] 萌萌哒
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3295
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// 这回只花了 1s 就打完了。
// 真好。记得多手造几组。最好有暴力对拍。
#include <bits/stdc++.h>
#define int long long
#define upp(a, x, y) for (int a = x; a <= y; a++)
#define dww(a, x, y) for (int a = x; a >= y; a--)
#define pb(x) push_back(x)
#define endl '\n'
#define x first
#define y second
#define PII pair<int, int>
using namespace std;
const int N = 1e6 + 10, X = 1e9 + 7;
int qmi(int a, int b, const int P) {
int res = 1;
while (b) {
if (b & 1) res = res * a % P;
a = a * a % P;
b >>= 1;
}
return res;
}
int fa[N][21], n, m;
int find(int x, int k) {
if (fa[x][k] == x) return x;
return fa[x][k] = find(fa[x][k], k);
}
void solve() {
cin >> n >> m;
upp(j, 0, 20) upp(i, 1, n - (1 << j) + 1) fa[i][j] = i;
upp(i, 1, m) {
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
int len = log2(r1 - l1 + 1);
fa[find(l1, len)][len] = find(l2, len);
fa[find(r1 - (1 << len) + 1, len)][len] =
find(r2 - (1 << len) + 1, len);
}
int cnt = 0;
dww(k, 20, 1) {
upp(i, 1, (n - (1 << k) + 1)) {
int fx = find(i, k);
fa[find(i, k - 1)][k - 1] = find(fx, k - 1);
fa[find(i + (1 << k - 1), k - 1)][k - 1] =
find(fx + (1 << k - 1), k - 1);
}
}
upp(i, 1, n) {
if (find(i, 0) == i) cnt++;
}
cout << (qmi(10, (cnt - 1), X) * 9) % X << endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int tt = 1;
while (tt--) solve();
return 0;
}

浙公网安备 33010602011771号