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;
}
原文地址:https://www.cnblogs.com/shawk/p/12832090.html