2019牛客暑期多校训练营(第八场场)_E题 Explorer(线段树+可撤销并查集)

题目链接: https://ac.nowcoder.com/acm/contest/888/E

题意: 给你一张无向图,每条边ui,vi的权值范围为[Li,Ri],要经过这条边的条件是你的容量要在[Li,Ri],现在问你你有多少种容量使得你可以从1走到n。

思路: 首先会想到离散化,离散l, r, 然后用线段树去维护这个区间(区间左闭,右开),每个节点存放这个容量可以通过哪些路径,最后遍历这颗树,使用可撤销并查集,来判断1是否能到n然后统计答案!

参考博客

代码

第一次写可撤销并查集,代码有些乱,别见怪

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
vector<pair<int, int> > node[MAXN<<2];
vector<int> ve;
int ans, n, m, tot;
int vis[MAXN], num[MAXN];
struct edge
{
    int u;
    int v;
    int l;
    int r;
}em[MAXN];

struct info
{
    int u;
    int v;
    int w;
};
inline void init()
{
    ve.clear();
}
inline void Build(int root, int l, int r)
{
    node[root].clear();
    if(l == r) return ;

    int mid = (l + r) >> 1;

    Build(root<<1, l, mid);
    Build(root<<1|1, mid + 1, r);
}

inline void updata(int root, int l, int r, int L, int R, pair<int, int> add)
{
    if(L <= l && r <= R){
        node[root].push_back(add);
        return ;
    }

    int mid = (l + r) >> 1;

    if(L <= mid){
        updata(root<<1, l, mid, L, R, add);
    }
    if(R > mid){
        updata(root<<1|1, mid + 1, r, L, R, add);
    }
}

inline int getID(int x)
{
    return (lower_bound(ve.begin(), ve.end(), x) - ve.begin() +  1);
}

inline int findx(int x)
{
    int r = x;
    while(r != vis[r]){
        r = vis[r];
    }

    return r;
}

inline info marge(int a, int b)
{
    int x = findx(a), y = findx(b);

    if(x == y) return {-1, -1, -1};
    if(num[x] > num[y]) swap(x, y);

    info tmp = {x, y, num[y]};

    vis[x] = y;
    num[y] = max(num[y] + 1, num[x] + 1);

    return tmp;
}

void dfs(int root, int l, int r)
{
    stack<info> st;
    int len = node[root].size();
    //并查集
    for(int i = 0; i < len; i++){
        info tmp = marge(node[root][i].first, node[root][i].second);
        if(tmp.u != -1) st.push(tmp);
    }
    //可以从1到n
    if(findx(1) == findx(n)){
        ans += ve[r] - ve[l - 1];
    }
    else if(l != r){
        int mid = (l + r) >> 1;
        dfs(root<<1, l, mid);
        dfs(root<<1|1, mid + 1, r);
    }
    //撤销并查集
    while(st.size()){
        info tmp = st.top();
        st.pop();
        vis[tmp.u] = tmp.u;
        vis[tmp.v] = tmp.v;
        num[tmp.v] = tmp.w;
    }
}
int main()
{
    ios::sync_with_stdio(false);

    while(cin >> n >> m){
        init();
        for(int i = 1; i <= m; i++){
            cin >> em[i].u >> em[i].v >> em[i].l >> em[i].r;
            em[i].r += 1;
            ve.push_back(em[i].l);
            ve.push_back(em[i].r);
        }
        //离散化
        sort(ve.begin(), ve.end());
        ve.erase(unique(ve.begin(), ve.end()), ve.end());
        tot = ve.size();

        Build(1, 1, tot);

        for(int i = 1; i <= m; i++){
            //左闭 右开
            updata(1, 1, tot, getID(em[i].l), getID(em[i].r) - 1, make_pair(em[i].u, em[i].v));
        }
        ans = 0;
        //初始化
        for(int i = 1; i <= n; i++){
            vis[i] = i;
            num[i] = 0;
        }
        dfs(1, 1, tot);
        cout << ans << endl;
    }
    return 0;
}

上一篇:c – 为什么我没有收到WM_MENUCHAR消息?


下一篇:显示隐藏文件.reg