zoukankan      html  css  js  c++  java
  • 「长乐集训 2017 Day1」区间 线段树

    题目

    对于两个区间((a,b),(c,d)),若(c < a < d)(c < b < d)则可以从((a,b))走到((c,d))去,现在有以下两种操作:

    • 给定(1 space x space y),表示在集合中添加((x,y))这个区间,保证新加入的这个区间一定比之前的所有区间长度长。
    • 给定(2 space a space b),表示询问是否有一条路径能从第(a)个区间走到第(b)个区间。

    初始时区间集合为空,现在请你回答所有的询问
    (1 leq n leq 10^5,所有数字绝对值 leq 10^9)

    题解:

    容易发现新加入的区间与其左右端点落到的区间一定是可以互达的。
    所以可以合并其集合。
    那么对于新加入的区间所覆盖的区间来说,多了一条到新加入区间的单向边。
    我们可以统计记录所有集合的最小左端点和最大右端点来判断一个区间是否可以通过有向边到达另一个区间。

    对于区间的维护我们可以使用并查集。
    同时使用线段树来维护插入区间和查询覆盖单点的所有区间的操作。

    由于每次进行完合并操作后点上的所有的区间都会合并为一个。
    所以总体复杂度(O(nlog^2n))

    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
        x=0;static char ch;static bool flag;flag = false;
        while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
        while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    #define rg register int
    #define rep(i,a,b) for(rg i=(a);i<=(b);++i)
    #define per(i,a,b) for(rg i=(a);i>=(b);--i)
    const int maxn = 100010;
    int L[maxn],R[maxn],op[maxn];
    int c[maxn<<1],cnt = 0,fa[maxn];
    int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
    inline void Union(int u,int v){
        int x = find(u),y = find(v);
        if(x == y) return ;
        fa[x] = y;
        L[y] = min(L[y],L[x]);
        R[y] = max(R[y],R[x]);
        return ;
    }
    vector<int>ve[maxn<<3];
    void modify(int rt,int l,int r,int p,int id){
        for(vector<int>::iterator it = ve[rt].begin();it != ve[rt].end();++it) Union(*it,id);
        if(ve[rt].empty() == false) ve[rt].clear(),ve[rt].push_back(find(id));
        if(l == r) return ;
        int mid = l+r >> 1;
        if(p <= mid) modify(rt<<1,l,mid,p,id);
        else modify(rt<<1|1,mid+1,r,p,id);
    }
    void insert(int rt,int l,int r,int L,int R,int id){
        if(L <= l && r <= R){ve[rt].push_back(id);return ;}
        int mid = l+r >> 1;
        if(L <= mid) insert(rt<<1,l,mid,L,R,id);
        if(R >  mid) insert(rt<<1|1,mid+1,r,L,R,id);
    }
    int idx[maxn],num = 0;
    int main(){
        freopen("interval.in","r",stdin);
        freopen("interval.out","w",stdout);
        int n;read(n);
        rep(i,1,n){
        	read(op[i]);read(L[i]);read(R[i]);
        	if(op[i] == 1) c[++cnt] = L[i],c[++cnt] = R[i];
        }
        sort(c+1,c+cnt+1);
        rep(i,1,n){
        	if(op[i] == 1){
        	    int l = lower_bound(c+1,c+cnt+1,L[i]) - c;
        	    int r = lower_bound(c+1,c+cnt+1,R[i]) - c;
        	    idx[++ num] = i;fa[i] = i;
        	    modify(1,1,cnt,l,i);modify(1,1,cnt,r,i);
        	    if(l+1 <= r-1) insert(1,1,cnt,l+1,r-1,i);
        	}else{
        	    int u = find(idx[L[i]]);
        	    int v = find(idx[R[i]]);
        	    //printf("u = %d,v = %d
    ",u,v);
        	    if(u == v) puts("YES");
        	    else if(L[v] < L[u] && L[u] < R[v]) puts("YES");
        	    else if(L[v] < R[u] && R[u] < R[v]) puts("YES");
        	    else puts("NO");
        	}
        }
        return 0;
    }
    
  • 相关阅读:
    LOJ-10108(欧拉回路+并查集)一个图至少用几笔画成
    hdu-1878(欧拉回路)
    LOJ-10106(有向图欧拉回路的判断)
    欧拉回路
    LOJ-10105(欧拉回路模板,套圈法,递归)
    LOJ-10102(求A到B之间的割点)
    LOJ-10103(求删去割点后最多的连通分量)
    LOJ-10102(桥的判断)
    【XSY2278】【HDU5669】the Red Sun(线段树+dijkstra)
    【XSY2434】【CF787D】遗产(线段树+dijkstra)
  • 原文地址:https://www.cnblogs.com/Skyminer/p/7101229.html
Copyright © 2011-2022 走看看