E. Explorer

Explorer

题目大意

给定一个n个点m条边的无向图。每个边拥有四个属性,u,v,l,r分别表示为连接u,v两点的一条边,其只能通过体型在[l,r]的人。问从1n有多少体型可以通过。

分析

蛮有意思的一道题目。我尽量以我的视角来说明白这道题的思路。

我们考虑,最直接的思路。我们将从1n的所有所有路都走一边,然后走的时候直接求一个区间交。但是时间复杂度一定是过不去的了。

我们反过头看一下,我们要求的区间交,我们看看能否将所有区间铺平,将其离散化。

接下来,我们思考如何处理我们的边。一个边的影响是在某个范围内可以将uv连接起来。

则我们的问题就转化为了,哪些区间的交可以使得我们的1n联通。

这个并没有什么好办法,我们只能考虑暴力求解了。

我们考虑利用线段树,以离散化后的区间的坐标建树,其中维护的是当前区间内可以连接起来的端点。

这样我们直接从线段树的根节点,向下爆搜,以此来代替求区间交的过程。在这个过程中,我们维护并查集,来帮助我们完成在搜到叶节点后判断1n是否是联通的。但是因为我们会回溯,因此我们需要维护的不是普通的并查集,而是可撤销并查集

其中有一些细节。我们假设alls是离散化后的数组。

  1. 如果我们离散区间时,是以lr来进行的。我们考虑一下统计答案时,如果是alls[x]和alls[x+1][l1,l2),那我们其实直接用alls[x+1]-alls[x]即可。但是如果是alls[x]和alls[x+1][l1,r1],那就尴尬了,因为这个右端点其实我们是要取到的。这里我们用了很巧妙的方式,我们插入的时候直接插入[l,r+1]这样,我们就解决了那个问题。
  2. 另一个细节是边界问题,我们考虑一下,如果取到最后一个区间,那我们就直接+1即可。

来看看代码。

Ac_code

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
struct DSU
{
    int fa[N];
    int sz[N];
    vector<pair<int&, int>>his_sz;
    vector<pair<int&, int>>his_fa;
    void init(int n) {
        for (int i = 1; i <= n; i++)fa[i] = i, sz[i] = 1;
    }
    int find(int x) {
        while (x != fa[x])x = fa[x];
        return x;
    }
    bool same(int u, int v) {
        return find(u) == find(v);
    }
    void merge(int u, int v) {
        int x = find(u);
        int y = find(v);
        if (x == y) return;
        if (sz[x] < sz[y]) std::swap(x, y);
        his_sz.push_back({ sz[x], sz[x] });
        sz[x] = sz[x] + sz[y];
        his_fa.push_back({ fa[y],fa[y] });
        fa[y] = x;
    }
 
    int histroy() {
        return his_fa.size();
    }
 
    void roll(int h) {
        while (his_fa.size() > h) {
            his_fa.back().first = his_fa.back().second;
            his_fa.pop_back();
            his_sz.back().first = his_sz.back().second;
            his_sz.pop_back();
        }
    }
    
}dsu;
struct Seg
{
    int l,r;
    vector<pair<int,int>> res;
}tr[N<<2];
struct Node
{
    int u,v,l,r;
}p[N];
vector<int> alls;
int n,m,ans,cnt;

int get(int x)
{
    return lower_bound(alls.begin(),alls.end(),x) - alls.begin() + 1;
}

void build(int u,int l,int r)
{
    tr[u] = {l,r};
    if(l==r) return ;
    int mid = l + r >> 1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}

void modify(int u,int l,int r,int x,int y)
{
    if(tr[u].l==l&&tr[u].r==r)
    {
        tr[u].res.push_back({x,y});
        return ;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(r<=mid) modify(u<<1,l,r,x,y);
    else if(l>mid) modify(u<<1|1,l,r,x,y);
    else modify(u<<1,l,mid,x,y),modify(u<<1|1,mid+1,r,x,y);
}

void dfs(int i,int l,int r,int res){
    int cnt = res;
    for(auto [x,y]:tr[i].res) 
        if(!dsu.same(x,y))
        {
            res++;
            dsu.merge(x,y);
        }
    if(l==r){
        if(dsu.find(1)==dsu.find(n)) 
            if(r<alls.size()) ans+=alls[r]-alls[l-1];
            else ans++;
        dsu.roll(cnt);
        return ;
    }
    int mid=l+r>>1;
    dfs(i<<1,l,mid,res);
    dfs(i<<1|1,mid+1,r,res);
    dsu.roll(cnt);
}

int main()
{
    ios;
    cin>>n>>m;dsu.init(n);
    for(int i=1;i<=m;i++)
    {
        int u,v,l,r;
        cin>>u>>v>>l>>r;
        p[i] = {u,v,l,r};
        alls.push_back(l),alls.push_back(r);
    }
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());
    cnt = alls.size();
    build(1,1,cnt);
    for(int i=1;i<=m;i++)
    {
        int l = get(p[i].l),r = get(p[i].r);
        modify(1,l,r,p[i].u,p[i].v);  
    }
    dfs(1,1,cnt,0);
    cout<<ans<<'\n';
    return 0;
}
posted @ 2022-10-08 11:01  艾特玖  阅读(21)  评论(0)    收藏  举报