其他分享
首页 > 其他分享> > [SCOI2016] 萌萌哒

[SCOI2016] 萌萌哒

作者:互联网

题目链接:

传送门

题目分析:

一道并查集好题
首先考虑暴力思路:由于每次给定区间内的数字要一一相等,把每个数字看成一个点,需要相等的数字就可以合并成一个点,用并查集维护
最后计算独立集合的个数,答案为\(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\n");
        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\n", ans%mod);
    return 0;
}

标签:cnt,get,int,萌萌,father,fa,base,SCOI2016
来源: https://www.cnblogs.com/kma093/p/10807659.html