20221111_T1B_线段树优化建图/并查集

题意

给定一个字符串,其中只有 a 和 b,现在一个字符能够跳到与之中间 a 的个数范围在 \([l,r]\) 的东西。

题解

赛时得分:100/100

对于一个东西,显然如果将能相互到达连边,那么一个点肯定是向后面和前面的一个区间连边的。

这个区间得到,我们可以通过二分解决。

普通是暴力枚举这些东西然后用并查集合并。这样复杂度显然是 \(\mathcal{O}(n^2)\) 的。我们思考线段树优化建图(毕竟是一个区间)

看如下图建边过程

image

image

image

这样建图之后我们满足了建图复杂度是 \(\mathcal{O}(n\log n)\) 但是怎么查询呢?

我们发现我们建边只会是一个点向另一个点或者是方框建边,那么我们固定一个点,另一个点向上跳,看看有没有固定在一起的就好了。

所以总时间复杂度 \(\mathcal{O}(n\log n)\)

题解是 \(\mathcal{O}(n)\) 是根据 \(k\) 的大小分类。我不会,爬了。

出题人说这方法只有 45 分,但是卡不掉。

代码

#include <bits/stdc++.h>
using namespace std;
template <typename T>inline void read(T& t){t=0; register char ch=getchar(); register int fflag=1;while(!('0'<=ch&&ch<='9')) {if(ch=='-') fflag=-1;ch=getchar();}while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;}
template <typename T,typename... Args> inline void read(T& t, Args&... args) {read(t);read(args...);}
const int N = 2e6 + 10, inf = 0x3f3f3f3f;

int n, l, r, q, sum[N], lc[N << 2], rc[N << 2], id[N << 2];
string st;
int fa[N << 2], rnk[N << 2];
bool biao[N << 2];

inline int findf(int x) {
    if(fa[x] == x) return x;
    else return fa[x] = findf(fa[x]);
}
inline void merge(int x, int y) {
    int fx = findf(x), fy = findf(y);
    if(fx == fy) return;
    if(rnk[fx] >= rnk[fy]) swap(fx, fy), swap(x, y);
    fa[fx] = fy; rnk[fy] += fx;
} 
inline bool check(int x, int y) {return findf(x) == findf(y);}
inline void build(int u, int l, int r) {
    // u 就是这个点的标号了
    lc[u] = l, rc[u] = r;
    if(l == r) {
        id[l] = u;
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
inline void adde(int u, int l, int r, int idu) {
    if(l <= lc[u] && r >= rc[u]) {
        merge(u, idu);
        biao[u] = 1;
        return;
    }
    if(l > rc[u] || lc[u] > r) return;
    adde(u << 1, l, r, idu), adde(u << 1 | 1, l, r, idu);
}
inline bool Find(int u, int idu) {
    if(findf(u) == findf(idu)) return 1;
    else if(u != 1) return Find(u >> 1, idu);
    return 0;
}
inline bool upup(int u1, int u2) {
    if(u1 == 1) return 0;
    if(u1 != u2) return upup(u1 >> 1, u2 >> 1);
    else if(biao[u1]) return 1;
    else return upup(u1 >> 1, u2 >> 1);
}
inline void Down(int u) {
    if(lc[u] == rc[u]) return;
    if(fa[u] != u) merge(u, u << 1), merge(u << 1, u << 1 | 1);
    Down(u << 1), Down(u << 1 | 1);
    return;
}

int main() {
    freopen("virtual.in", "r", stdin);
    freopen("virtual.out", "w", stdout);
    cin >> n >> l >> r;
    cin >> st;
    st = '*' + st;
    for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + (st[i] == 'a');
    build(1, 1, n);
    for(int i = 1; i <= (n << 2); ++i) fa[i] = i, rnk[i] = 1;
    for(int i = 1; i <= n; ++i) {
        int Le = lower_bound(sum + 1, sum + n + 1, l + sum[i - 1]) - sum, Ri = upper_bound(sum + 1, sum + n + 1, r + sum[i - 1]) - sum - 1;
        if(Le <= Ri) adde(1, Le, Ri, id[i]);
        Le = lower_bound(sum, sum + n + 1, sum[i] - r) - sum + 1, Ri = lower_bound(sum, sum + n + 1, sum[i] - l + 1) - sum;
        if(Le <= Ri) adde(1, Le, Ri, id[i]);
    }
    // }
    Down(1);
    read(q);
    for(int i = 1; i <= q; ++i) {
        int x, y;
        read(x, y);
        if(Find(id[x], id[y]) || Find(id[y], id[x])) puts("Yes");
        else puts("No");
    } 
    return 0;
}
posted @ 2022-11-11 15:21  Mercury_City  阅读(42)  评论(1编辑  收藏  举报