bzoj 4569 [Scoi2016]萌萌哒 并查集 + ST表

题目链接

Description

一个长度为(n)的大数,用(S_1S_2S_3...S_n)表示,其中(S_i)表示数的第(i)位,(S_1)是数的最高位,告诉你一些限制条件,每个条件表示为四个数,(l_1,r_1,l_2,r_2),即两个长度相同的区间,表示子串(S_{l1}S_{l1+1}S_{l1+2}...S_{r1})(S_{l2}S_{l2+1}S_{l2+2}...S_{r2})完全相同。

问满足以上所有条件的数有多少个。

思路

参考

https://www.cnblogs.com/owenyu/p/7146428.html

暴力想法

对于每一个条件,将([l_1,r_1])([l_2,r_2])范围内对应的数一一(union)起来,最后检查有多少个集合(cnt),答案即为(10^{cnt-1}*9).

优化

运用(ST)表的思路,但形式与之相反。

一般的(ST)表是从小区间中衍伸得到大区间的性质,本题则是从大区间向小区间推。

(logn)个并查集,对于每一个条件,拆成前后两部分分别进行(union)

最后从上往下推,将每层的每个大区间拆成下一层的两个小区间。

最后检查最底层。

Code

#include <bits/stdc++.h>
#define F(i, a, b) for (int i = (a); i < (b); ++i)
#define F2(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i > (b); --i)
#define dF2(i, a, b) for (int i = (a); i >= (b); --i)
#define maxn 100000
#define maxk 17
using namespace std;
typedef long long LL;
bool vis[maxn+10];
int sz[maxk+1][maxn+10], fa[maxk+1][maxn+10], Log[maxn+10], bi[maxk+1], n, m;
inline int find(int dep, int x) {
    return fa[dep][x]==x ? x : fa[dep][x]=find(dep, fa[dep][x]);
}
inline void unionn(int dep, int x, int y) {
    x = find(dep, x), y = find(dep, y);
    if (x==y) return;
    if (sz[dep][x]>sz[dep][y]) swap(x, y);
    fa[dep][x] = y;
}
void init() {
    Log[0] = -1; bi[0] = 1;
    F2(i, 1, maxn) Log[i] = Log[i>>1]+1;
    int k=Log[n];
    F2(i, 1, maxk) {
        bi[i] = bi[i-1]<<1;
    }
    F2(i, 0, maxk) F2(j, 1, n) fa[i][j]=j, sz[i][j]=1;
}
const LL mod=1e9+7;
LL poww(LL a, LL b) {
    LL ret=1;
    while (b) {
        if (b&1) (ret*=a)%=mod;
        (a*=a)%=mod;
        b>>=1;
    }
    return ret;
}
int main() {
    scanf("%d%d", &n,&m);
    init();
    F(i, 0, m) {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        int dep=Log[r1-l1+1];
        unionn(dep, l1, l2);
        unionn(dep, r1-bi[dep]+1, r2-bi[dep]+1);
    }
    dF(dep, maxk, 0) {
        F2(i, 1, n) {
            int x=find(dep, i);
            unionn(dep-1, i, x);
            unionn(dep-1, i+bi[dep-1], x+bi[dep-1]);
        }
    }
    int ans=0;
    F2(i, 1, n) ans += (find(0, i)==i);
    printf("%lld
", poww(10, ans-1)*9%mod);
    return 0;
}

原文地址:https://www.cnblogs.com/kkkkahlua/p/8516377.html