$bzoj4569$

$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;
}
View Code

 

posted @ 2018-01-28 22:16  19992147  阅读(89)  评论(0编辑  收藏  举报