牛客第八场 E Explorer —— 可撤销并查集 + 线段树

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n 个点, m m m 条边
    每条边有一个范围,编号在这个范围里的人能通过这条边
    问最多有多少人能从 1 1 1 走到 n n n

解题思路:

    从未见过的船新思路
    考虑枚举区间
    线段树的每个结点表示能覆盖当前区间的边的编号
    然后暴力 d f s dfs dfs 整颗树,每到一个结点,将所有边的两点放到并查集里
    注意从一个结点返回的时候,要回撤并查集
    所以要用按秩合并,如果 1 1 1 n n n 在一个集里,则加入到答案里

    时间复杂度: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

核心:线段树 + 可撤销并查集维护图

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 998244353;
const int maxn = 2e5 + 10;
int n, m, cnt = 1, ls[maxn<<2];
int f[maxn], siz[maxn], ans;
vector <int> t[maxn<<2];
struct node{
    int u, v, l, r;
} a[maxn];
 
int getf(int x){
    return x==f[x] ? x : getf(f[x]);
}
 
void update(int L, int R, int c, int l, int r, int rt){
    if(L<=l && r<=R){
        t[rt].push_back(c);
        return;
    }
    int m = l + r >> 1;
    if(L <= m) update(L, R, c, l, m, rt<<1);
    if(R > m) update(L, R, c, m+1, r, rt<<1|1);
}
 
void dfs(int l, int r, int rt){
    vector <pii> lastf;
    for(int i=0; i<t[rt].size(); i++){
        int x = a[t[rt][i]].u, y = a[t[rt][i]].v;
        x = getf(x), y = getf(y);
        if(x == y) continue;
        if(siz[x] > siz[y]) swap(x, y);
        f[x] = y; int d = 0;	//	按秩合并 
        if(siz[x] == siz[y]) siz[y]++, d++;
        lastf.push_back({x, d});
    }
    int m = l + r >> 1;
    if(getf(1)==getf(n)) ans += ls[r+1] - ls[l];
    else if(l < r) dfs(l,m,rt<<1), dfs(m+1,r,rt<<1|1);
     
    for(int i=lastf.size()-1; ~i; i--){
        siz[f[lastf[i].first]] -= lastf[i].second;
        f[lastf[i].first] = lastf[i].first;
    }
    lastf.clear();
}
 
int main() {
    scanf("%d%d", &n, &m);
    for(int i=0; i<=n; i++) f[i] = i, siz[i] = 1;
    for(int i=0; i<m; i++){
        scanf("%d%d", &a[i].u, &a[i].v);
        scanf("%d%d", &a[i].l, &a[i].r);
        ls[cnt++] = a[i].l;
        ls[cnt++] = a[i].r + 1;
    }
    sort(ls+1, ls+cnt);
    cnt = unique(ls+1, ls+cnt) - ls - 1;
    for(int i=0; i<m; i++){
        update(lower_bound(ls+1,ls+cnt+1,a[i].l)-ls, \
        lower_bound(ls+1, ls+cnt+1, a[i].r+1)-ls-1, i, 1, cnt, 1);
    }
    dfs(1, cnt, 1);
    printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值