cf1284D——线段树,排序

Posted zsben991126

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cf1284D——线段树,排序相关的知识,希望对你有一定的参考价值。

/*
原问题可以简化成:给定n对区间[sai,eai],[sbi,ebi],
            是否存在i,j,使[sai,eai],[saj,eaj] 与[sbi,ebi],[sbj,ebj]有且仅有一组相交
 
思路:遍历第i对区间,先找到a部分和[sai,eai]相交的所有段,设这个段的集合为S,
      由于题意,S中所有b部分的段都要和[sbi,ebi]相交,这就要求S中最小的ebj>=sbi,最大的sbj<=ebi
      所以想到一开始就对所有的段,按照eai升序排序,然后二分找到[x,i-1]的段a部分和i的a部分相交,
      再用线段树查询b部分两端的极值,和bi进行比较即可 
最后swap一下ab再算一次就好 
*/
#include<bits/stdc++.h>
#define N 200005
using namespace std;
struct Seg{int sa,ea,sb,eb;}p[N];
int cmp(Seg &a,Seg &b){
    if(a.ea!=b.ea)return a.ea<b.ea;
    else return a.sa<b.sa;
}
int n,mx[4*N], mi[4*N];
void creat(int l, int r, int k)
{
    if(l == r){
        mx[k]=p[l].sb, mi[k]=p[l].eb;
        return ;
    }
    int mid = (l+r)/2;
    creat(l, mid, 2*k);
    creat(mid+1, r, 2*k+1);
    mx[k] = max(mx[2*k], mx[2*k+1]);
    mi[k] = min(mi[2*k], mi[2*k+1]);
}
 
int querymin(int l, int r, int al, int ar, int k)
{
    if(l == al && r == ar)return mi[k];
    int mid = (l+r)/2;
    if(ar <= mid)return querymin(l, mid, al, ar, 2*k);
    else if(al > mid)return querymin(mid+1, r, al, ar, 2*k+1);
    else return min(querymin(l, mid, al, mid, 2*k),
                    querymin(mid+1, r, mid+1, ar, 2*k+1));
}
 
int querymax(int l, int r, int al, int ar, int k)
{
    if(l == al && r == ar)return mx[k];
    int mid = (l+r)/2;
    if(ar <= mid)return querymax(l, mid, al, ar, 2*k);
    else if(al > mid)return querymax(mid+1, r, al, ar, 2*k+1);
    else return max(querymax(l, mid, al, mid, 2*k),
                    querymax(mid+1, r, mid+1, ar, 2*k+1));
} 
 
int ea[N];
int solve(){
    std::sort(p+1,p+1+n,cmp);
        creat(1,n,1);
    for(int i=1;i<=n;i++)ea[i]=p[i].ea;
    for(int i=1;i<=n;i++){
        if(i==1)continue;
        int left = p[i].sa;
        
        //二分找到
        int ans=lower_bound(ea+1,ea+i,left)-ea;
        if(ans>i-1)continue;
        
        //判断b部分是否合法 
        int maxL=querymax(1,n,ans,i-1,1);
        int minR=querymin(1,n,ans,i-1,1);
        if(minR<p[i].sb || maxL>p[i].eb)
            return 0;
    }
    return 1;
}
 
int main(){
/*    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>p[i].sa>>p[i].ea>>p[i].sb>>p[i].eb;
    int res=solve(); 
    for(int i=1;i<=n;i++)
        swap(p[i].sa,p[i].sb),swap(p[i].ea,p[i].eb);
    res &= solve();
    
    if(res)puts("YES");
    else puts("NO");
*/
    int  i, j, k, sig = 1;
    scanf("%d", &n);
    for(i=1;i<=n;i++)
        scanf("%d %d %d %d", &p[i].sa, &p[i].ea, &p[i].sb, &p[i].eb);
    sig &= solve();
    for(i=1;i<=n;i++)
        swap(p[i].sa, p[i].sb), swap(p[i].ea, p[i].eb);
    sig &= solve();
    if(sig)printf("YES
");
    else printf("NO
");
}

以上是关于cf1284D——线段树,排序的主要内容,如果未能解决你的问题,请参考以下文章

CF558E A Simple Task 线段树

线段树双tag+差分数组——cf1208E

[CF1284D] New Year and Conference - 扫描线

线段树逆序对(偏序)——cf1187D好题!

CF799EAquarium decoration 线段树

CF gym 100962D Deep Purple [后缀树,树链剖分,线段树]