CF319E Ping-Pong

Link
如果两个区间相交,那么这两个区间之间有双向边。
如果一个区间包含另一个区间,那么被包含的区间向大区间有一条单向边。
考虑用并查集把所有以双向变连边的区间合并成一个大区间,这可以用线段树实现。
可以证明从一个区间到另一个区间的路径最多经过一条单向边,直接对两个区间所属的大区间进行判断即可。

#include<cctype>
#include<cstdio>
#include<vector>
const int N=100007;
int root,cnt,tot,ch[N*40][2];
struct node{int fa,l,r,size;}t[N];
std::vector<int>vec[N*40];
int read(){int x=0,c=getchar(),f=1;while(isspace(c))c=getchar();if(c=='-')f=-1,c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return f*x;}
int find(int x){return x==t[x].fa? x:t[x].fa=find(t[x].fa);}
void merge(int u,int v){if((u=find(u))^(v=find(v)))t[u].fa=v,t[v].l=std::min(t[v].l,t[u].l),t[v].r=std::max(t[v].r,t[u].r),t[v].size+=t[u].size;}
#define mid ((l+r)>>1)
void update(int&p,int l,int r,int L,int R,int x)
{
    if(l>R||L>r||L>R) return ;
    if(!p) p=++tot;
    if(L<=l&&r<=R) return vec[p].push_back(x);
    update(ch[p][0],l,mid,L,R,x),update(ch[p][1],mid+1,r,L,R,x);
}
void link(int p,int l,int r,int x,int u)
{
    for(auto v:vec[p]) merge(v,u);
    if(vec[p].size()>1) vec[p].resize(1);
    if(!p) return ;
    x<=mid? link(ch[p][0],l,mid,x,u):link(ch[p][1],mid+1,r,x,u);
}
#undef mid
void update(){int l=read(),r=read();++cnt,t[cnt]={cnt,l,r,1},update(root,-1e9,1e9,l+1,r-1,cnt),link(root,-1e9,1e9,l,cnt),link(root,-1e9,1e9,r,cnt);}
void query(){int u=find(read()),v=find(read());puts(t[v].l<=t[u].l&&t[u].r<=t[v].r&&!(t[u].l==t[v].l&&t[u].r==t[v].r&&t[u].size==1)?"YES":"NO");}
int main(){for(int n=read();n;--n) read()==1? update():query();}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12754835.html