$st表+并查集$
$考虑暴力方法:我们每次将对应相等的位置用并查集连起来,那么最终答案就是9*10^{连通块个数-1}$
$很明显上面这个办法过不去,问题在于重复次数太多了,如果一个区间已经对应相等了就不用再次连,用st表优化这个过程$
$每次向st表一样递归连接,分成log层,每层维护\frac{n}{logn}个并查集,代表区间,那么我们加入记忆化的思想,如果对应区间已经联通就返回,这个就是用并查集完成,否则继续递归进行这个过程。其实这里就是运用了记忆化的思想$
$那么很明显每层穿起来所有区间需要区间个数-1次,那么一共有nlogn个区间,复杂度也就是nlogn了$
$所以看见这种题就要考虑用一些方式记忆化从而降低复杂度$
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 5, P = 1e9 + 7; int n, m; int fa[18][N], Log[N]; int find(int p, int x) { return x == fa[p][x] ? x : fa[p][x] = find(p, fa[p][x]); } void merge(int k, int a, int b) { int x = find(k, a), y = find(k, b); if(x == y) { return; } fa[k][x] = y; if(!k) { return; } merge(k - 1, a, b); merge(k - 1, a + (1 << k - 1), b + (1 << k - 1)); } int main() { scanf("%d%d", &n, &m); if(n == 1) { puts("10"); return 0; } for(int j = 0; j <= 17; ++j) { for(int i = 1; i + (1 << j) - 1 <= n; ++i) { fa[j][i] = i; } } for(int i = 2; i <= n; ++i) { Log[i] = Log[i >> 1] + 1; } while(m--) { int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d); int t = Log[b - a + 1]; merge(t, a, c); merge(t, b - (1 << t) + 1, d - (1 << t) + 1); } long long ans = 9, f = 0; for(int i = 1; i <= n; ++i) { if(find(0, i) == i) { if(f) { ans = ans * 10 % P; } else { f = 1; } } } printf("%lld\n", ans); return 0; }