1. 程式人生 > 實用技巧 >CF1284D New Year and Conference(二分+線段樹)

CF1284D New Year and Conference(二分+線段樹)

這道題抽象出來的問題是,對於每一對點,如果他們的a區間不相交,那麼他們的b區間一定相交,反之亦然。

這啟發我們可以列舉每個點的a區間,查詢是否b區間相交。

快速找到與每個點的a區間相交的辦法就是按右端點排序後,在i之前的右端點大於i的左端點的這段區間,我們不必關注i之後的點,因為這些對會在後面的時候被計算到。

之後我們線上段樹上查詢這段區間即可。我們知道如果區間不相交,那麼最小的右端點會小於i的左端點或者最大的左端點大於i的右端點。因此我們維護區間最大最小值即可

#include<bits/stdc++.h>
using namespace std;
typedef long long
ll; typedef unsigned long long ull; typedef pair<ll,ll> pll; const int N=2e6+10; const int M=2e6+10; const int inf=0x3f3f3f3f; struct Q{ ll a,b,c,d; }s[N]; vector<int> num; int n; struct node{ int l,r; ll mi; ll mx; }tr[N<<1]; bool cmp(Q a,Q b){ if(a.b==b.b)
return a.a<b.a; return a.b<b.b; } void pushup(int u){ tr[u].mi=min(tr[u<<1].mi,tr[u<<1|1].mi); tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx); } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,s[l].d,s[l].c}; } else{ tr[u]={l,r};
int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } } ll query_max(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ return tr[u].mx; } int mid=tr[u].l+tr[u].r>>1; ll ans=0; if(l<=mid) ans=query_max(u<<1,l,r); if(r>mid) ans=max(ans,query_max(u<<1|1,l,r)); return ans; } ll query_min(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ return tr[u].mi; } int mid=tr[u].l+tr[u].r>>1; ll ans=1e9; if(l<=mid) ans=query_min(u<<1,l,r); if(r>mid) ans=min(ans,query_min(u<<1|1,l,r)); return ans; } int solve(){ sort(s+1,s+1+n,cmp); int i; num.clear(); for(i=1;i<=n;i++){ num.push_back(s[i].b); } build(1,1,n); for(i=1;i<=n;i++){ if(i==1) continue; int pos=lower_bound(num.begin(),num.end(),s[i].a)-num.begin()+1; if(pos>=i) continue; int mi=query_min(1,pos,i-1); int mx=query_max(1,pos,i-1); if(mi<s[i].c||mx>s[i].d) return false; } return true; } int main(){ ios::sync_with_stdio(false); cin>>n; int i; for(i=1;i<=n;i++){ cin>>s[i].a>>s[i].b>>s[i].c>>s[i].d; } int ok=1; ok&=solve(); for(i=1;i<=n;i++){ swap(s[i].a,s[i].c); swap(s[i].b,s[i].d); } ok&=solve(); if(ok){ cout<<"YES"<<endl; } else{ cout<<"NO"<<endl; } }
View Code