zoukankan      html  css  js  c++  java
  • 2H [SCOI2016]萌萌哒-——倍增维护并查集

    题目

    一个长度为n的大数,用S1S2S3...Sn表示,其中Si表示数的第i位,S1是数的最高位,告诉你一些限制条件,每个条
    件表示为四个数,l1,r1,l2,r2,即两个长度相同的区间,表示子串Sl1Sl1+1Sl1+2...Sr1与Sl2Sl2+1Sl2+2...S
    r2完全相同。比如n=6时,某限制条件l1=1,r1=3,l2=4,r2=6,那么123123,351351均满足条件,但是12012,13
    1141不满足条件,前者数的长度不为6,后者第二位与第五位不同。问满足以上所有条件的数有多少个。

    Input

    第一行两个数n和m,分别表示大数的长度,以及限制条件的个数。接下来m行,对于第i行,有4个数li1,ri1,li2
    ,ri2,分别表示该限制条件对应的两个区间。
    1≤n≤105,1≤m≤105,1≤li1,ri1,li2,ri2≤n;并且保证ri1-li1=ri2-li2。

    Output

    一个数,表示满足所有条件且长度为n的大数的个数,答案可能很大,因此输出答案模10^9+7的结果即可。

    Sample Input

    4 2
    1 2 3 4 
    3 3 3 3
    

    Sample Output

    90
    

    Solve

    • 暴力的话就是对于每个区间都扫一下,然后并查集合并,最后看有几个并查集,由于每个并查集里的数都得是一样的,第一位有9种选法,之后的都有10种,乘起来就好了

    • 时间复杂度nm,接受不了,考虑优化

    • 运用倍增的思想,k,x表示的从x开始的2的k次方的区间内的所有点,合并的时候就可以log的合并

    • 最后把合并下传下去就好了,挺妙的

    • 合并的时候我用的二进制拆分,其实用st表那种合并方式也行,而且会更快

    Code

    #include <cstdio>
    
    using namespace std;
    const int N = 1e5 + 5, M = 1e9 + 7;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    bool g;
    int n, m, f[20][N], ans;
    
    int Find(int i, int x) {
        return x == f[i][x] ? x : f[i][x] = Find(i, f[i][x]);
    }
    
    void Merge(int i, int x, int y) {
        f[i][Find(i, x)] = Find(i, y);
    }
    
    int main() {
        n = read(); m = read();
        for (int i = 0; i <= 16; ++i)
            for (int x = n - (1 << i) + 1; x >= 1; --x)
                f[i][x] = x;
        while (m--) {
            int l1 = read(), r1 = read(), l2 = read(), r2 = read();
            for (int i = 16; i >= 0; --i)
                if (l1 + (1 << i) - 1 <= r1)
                    Merge(i, l1, l2), l1 += 1 << i, l2 += 1 << i;
        }
        for (int i = 16; i >= 1; --i) {
            for (int x = n - (1 << i) + 1; x >= 1; --x) {
                int fx = Find(i, x);
                if (fx == x) continue;
                Merge(i - 1, x, fx);
                Merge(i - 1, x + (1 << i - 1), fx + (1 << i - 1));
            }
        }
        for (int i = 1; i <= n; ++i) {
            if (i != Find(0, i)) continue;
            if (!g) ans = 9, g = 1;
            else ans = 10ll * ans % M;
        }
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    安装centos后无法引导启动windows7的解决方法
    Ubuntu14.04 搜索不到WIFI或连接不上的解决方法。
    ubuntu磁盘分区问题
    Cenos7下nginx+mysql+php环境的搭建
    PHP发送邮件
    php使用数据库的并发问题(乐观锁与悲观锁)
    总结一些php的面试题
    遇到高并发一些处理方法
    PHP中9大缓存技术总结
    php并发加锁示例
  • 原文地址:https://www.cnblogs.com/shawk/p/12832090.html
Copyright © 2011-2022 走看看