2019牛客暑期多校训练营(第八场)Explorer(线段树 分治答案)

original link - https://ac.nowcoder.com/acm/contest/888/E

题意:

给出n个点,m条边,每条边上有个范围,指定范围的数字才能通过。现在点 1 1 1上有 [ 1 , 1 e 9 ] [1,1e9] [1,1e9]想要到达点 n n n,问最后有多少个点可以到达。

解析:

直接搜应该是不行地,我们考虑分治答案。

分治区间 [ L , R ] [L,R] [L,R],将所有包含该区间的边都连上,查看是否可以从 1 → n 1\to n 1n。这个可以用并查集来查询。

如果可以,说明 [ L , R ] [L,R] [L,R]可以计入答案;否则,我们去判断 [ L , m i d ] , [ m i d + 1 , R ] [L,mid],[mid+1,R] [L,mid],[mid+1,R]

tip:

- 并查集因为在回溯时要拆开,所以不能路径压缩,所以需要启发式合并。
- 我们需要将所有边加入对应的可行集合(包含 [ L , R ] [L,R] [L,R]后,加入 [ L , R ] [L,R] [L,R],之后就不用加入 [ L , m i d ] [L,mid] [L,mid]
- 区间加离散化,所以要将区间值存到点上,这个操作为: R + 1 , [ g e t ( L ) , g e t ( R + 1 ) − 1 ] R+1,[get(L),get(R+1)-1] R+1,[get(L),get(R+1)1] g e t get get离散化 i n d e x index index

代码:

#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)

const int maxn=2e5+5;
int n,m;

struct Uni{
    int num;
    int a[maxn];
    void push(int p){
        a[++num]=p;
    }
    void init(){
        sort(a+1,a+1+num);
        num=unique(a+1,a+1+num)-a-1;
    }
    int pos(int val){
        return lower_bound(a+1,a+1+num,val)-a;
    }
}U;

struct edge{
    int l,r,u,v;
}e[maxn];

#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
vector<int>tr[maxn<<2];
void insert(int rt,int l,int r,int L,int R,int id){
    if(l>=L&&r<=R){
        tr[rt].push_back(id);
        return ;
    }
    if(L<=mid)
        insert(ls,l,mid,L,R,id);
    if(R>mid)
        insert(rs,mid+1,r,L,R,id);
}

#define pill pair
namespace link{
    int fa[maxn];
    int siz[maxn];
    void init(int n){
        rep(i,1,n)fa[i]=i,siz[i]=1;
    }
    int fin(int a){
        return fa[a]==a?a:fin(fa[a]);
    }
    stack<pill>sta;
    bool un(int a,int b){
        int f1=fin(a),f2=fin(b);
        if(f1==f2)return 0;
        if(siz[f1]>siz[f2])swap(f1,f2);
        fa[f1]=f2;
        sta.push({f1,f2});
        siz[f2]+=siz[f1];
        return 1;
    }
    void undo(int tim){
        while(tim--){
            auto p=sta.top();sta.pop();
            siz[p.second]-=siz[p.first];
            fa[p.first]=p.first;
        }
    }
}
using namespace link;

int ans;
void query(int rt,int l,int r){ // 对每个段判断是否可行
    int ct=0;
    for(auto i:tr[rt]){
        int u=e[i].u,v=e[i].v;
        un(u,v)&&++ct;
    }
    if(fin(1)==fin(n)){
        ans+=U.a[r+1]-U.a[l];
    }
    else if(l==r){
        ;
    }
    else{
        query(ls,l,mid);
        query(rs,mid+1,r);
    }
    undo(ct);
}

int main(){
    scanf("%d%d",&n,&m);
    U.num=0;
    rep(i,1,m){
        scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
        U.push(e[i].l);
        U.push(e[i].r+1);
    }
    U.init();
    rep(i,1,m){
        e[i].l=U.pos(e[i].l);
        e[i].r=U.pos(e[i].r+1)-1;
        insert(1,1,U.num,e[i].l,e[i].r,i);
    }
    init(n);
    query(1,1,U.num);
    printf("%d\n",ans);
}

你可能感兴趣的:(其他算法,数据结构)