2H [SCOI2016]萌萌哒-——倍增+ST表

题目

一个长度为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≤10^5,1≤m≤10^5,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

题解

解题思路

运用并查集的思想,两个区间l1,r1,l2,r2,只需将l1,l2, r1,r2合并
最后看并查集的个数
f[x][i]表示x的第2^i个父亲

代码

#include <cstdio>
#include <cmath>
using namespace std;
const int N = 1e5+5, M = 1e9+7;
int n, m, f[N][20], ans, K;
int found(int x, int k) {
    return f[x][k] == x ? x : f[x][k] = found(f[x][k], k);
}
void merge(int x, int y, int k) {
    int fx = found(x, k), fy = found(y, k);
    if (fx != fy) f[fx][k] = fy;
}
int main() {
    scanf("%d%d", &n, &m);
    K = floor(log2(n));
    for(int i = 1; i <= n; i++)
        for(int k = 0; k <= K; k++)
            f[i][k] = i;
    for(int i = 1; i <= m; i++) {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        for(int k = K; ~k; k--)
            if (l1 + (1 << k) - 1 <= r1) {
                merge(l1, l2, k);
                l1 += 1 << k;
                l2 += 1 << k;
            }
    }
    for(int k = K; k; k--) 
        for(int x = 1; x + (1 << k) - 1 <= n; x++) {
            int fx = found(x, k);
            merge(x, fx, k - 1);
            merge(x + (1 << k-1), fx + (1 << k-1), k - 1);
        }
    for(int i = 1; i <= n; i++) 
        if (f[i][0] == i) 
            ans = !ans ?  9 : ans * 10ll % M;
    printf("%d", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Z8875/p/12832090.html