zoukankan      html  css  js  c++  java
  • [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
    ");
            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;
    }
    
  • 相关阅读:
    MongoDB简单使用
    mongodb安装部署
    分布式通信-序列化
    分布式通信协议
    分布式概念
    springboot-事件
    spring-事件
    spring-@Component/@ComponentScan注解
    springboot-Date日期时间问题
    enginx:基于openresty,一个前后端统一,生态共享的webstack实现
  • 原文地址:https://www.cnblogs.com/kma093/p/10807659.html
Copyright © 2011-2022 走看看