题目链接:##
题目分析:##
一道并查集好题
首先考虑暴力思路:由于每次给定区间内的数字要一一相等,把每个数字看成一个点,需要相等的数字就可以合并成一个点,用并查集维护
最后计算独立集合的个数,答案为(9*10^{k-1})(首位不为1,只有9种选择)
上述思路复杂度是(O(nm))的,显然跑不过,考虑优化
由数据范围想到(log)级别的算法,考虑倍增,以(ST)表的思路优化
将每次要合并的区间进行二进制拆分,并从大到小进行启发式合并,最后查询时将答案下放
代码:##
#include<bits/stdc++.h>
#define N (100000+5)
#define mod (1000000000+7)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c;
c = getchar();
while(!isdigit(c)) {
if (c == '-') f = -1;
c = getchar();
}
while(isdigit(c)) {
cnt = cnt * 10 + c - '0';
c = getchar();
}
return cnt * f;
}
int n, m, l1, l2, r1, r2, tot = 0;
int fa[N * 18], id[N][20], start[N * 18], base[20];
long long ans;
int get_father(int x) {
if(fa[x] == x) return x;
return fa[x] = get_father(fa[x]);
}
void merge(int x, int y) {
int fx = get_father(x);
int fy = get_father(y);
if(fx > fy) swap(fx, fy);
fa[fy] = fx; //启发式合并
}
int main() {
base[0] = 1;
for (register int i = 1; i <= 18; i++) base[i] = base[i-1] << 1;
n = read(); m = read();
if (n == 1) {
printf("10
");
return 0;
}
for (register int i = 0; i <= 18; i++)
for (register int j = 1; j + base[i] - 1 <= n; j++) {
id[j][i] = ++tot;
fa[tot] = tot;
start[tot] = j;
}
/*-------------合并---------------*/
for (register int i = 1; i <= m; i++) {
l1 = read(); r1 = read(); l2 = read(); r2 = read();
for (register int j = 18; j >= 0; --j) {
if(l1 + base[j] - 1 <= r1) {
merge(id[l1][j], id[l2][j]);
l1 += base[j]; l2 += base[j];
}
}
}
/*-------------下放-------------*/
for (register int j = 18; j >= 1; --j)
for (register int i = 1; i + base[j] - 1 <= n; i++) {
int f = get_father(id[i][j]); int s = start[f];
merge(id[i][j - 1], id[s][j - 1]);
merge(id[i + base[j - 1]][j - 1], id[s + base[j - 1]][j - 1]);
}
ans = 9;
for (register int i = 2; i <= n; i++) {
if(fa[id[i][0]] == id[i][0]) {
ans *= 10;
ans %= mod;
}
}
// for (register int i = 1; i <= 18; i++) printf("%d ",base[i]);
// return 0;
printf("%lld
", ans%mod);
return 0;
}