[KD树] BZOJ5005. 乒乓游戏

当两个区间交叉的时候,他们是可以相互达到的

当我们每插入一个区间的时候,把和它交叉的所有区间缩成一个区间,即这个区间的L为这些区间的L的最小值,R为这些区间的R的最大值

还有一种特殊情况,当我们插入一个区间时,如果它被之前的某个区间包含了,那么也要缩起来,因为题目保证新加入的区间长度一定大于之前的区间,那么如果它被某个区间包含了,那个区间一定是多个区间缩成的,这些区间一定会有一个区间和当前的区间交叉

那么现在就要找和它交叉的区间。

设当前区间为[L,R],那么要和它缩起来的区间[L1,R1]满足一下条件之一

  • L1 < L & L < R1 < R

  • L < L1 < R & R1 > R

  • L <= L1 & R1>= R

把区间看作点(L,R),那么每个条件对应二位平面内的一个矩形,用KD-TREE把这个矩形中的点提取出来,用并查集缩起来,每个点最多被提取一次,所以复杂度是 O(nn)

当然这样会被卡…那么就没 n 次操作后暴力重构一下KD-TREE,这样就比较科学?

UPD:好像替罪羊式重构复杂度更优

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
#define fi first
#define se second

using namespace std;

const int N=100010;

int n,q,fa[N],l[N],r[N];

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0; int f=1;
  for(;c>'9'||c<'0';c=nc())if(c=='-') f=-1;
  for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc()); x*=f;
}

vector<int> v;

namespace KDT{
  struct Pt{
    int x,y,maxx,maxy,minx,miny,used,g,size;
    Pt *l,*r;
  }pool[N],*t,*root;

  void dfs(Pt *cur){
    if(!cur) return ;
    if(!cur->used) cur->used=1,v.push_back(cur->g);
    dfs(cur->l); dfs(cur->r);
  }

  inline void Up(Pt *x){
    if(!x->used)
      x->maxx=x->minx=x->x,x->maxy=x->miny=x->y,x->size=1;
    else
      x->maxx=x->maxy=-1<<30,x->minx=x->miny=1<<30,x->size=0;
    if(x->l){
      x->size+=x->l->size;
      x->maxx=max(x->l->maxx,x->maxx),x->minx=min(x->minx,x->l->minx);
      x->maxy=max(x->l->maxy,x->maxy),x->miny=min(x->miny,x->l->miny);
    }
    if(x->r){
      x->size+=x->r->size;
      x->maxx=max(x->r->maxx,x->maxx),x->minx=min(x->minx,x->r->minx);
      x->maxy=max(x->r->maxy,x->maxy),x->miny=min(x->miny,x->r->miny);
    }
  }

  void Extract(Pt *&cur,int a,int b,int x,int y){
    if(!cur) return ;
    if(cur->maxx<=x && cur->maxy<=y && cur->minx>=a && cur->miny>=b){
      dfs(cur); cur=0; return ;
    }
    if(cur->maxx<a || cur->maxy<b || cur->minx>x || cur->miny>y) return ;
    if(cur->x>=a && cur->x<=x && cur->y>=b && cur->y<=y && !cur->used){
      cur->used=1; v.push_back(cur->g);
    }
    Extract(cur->l,a,b,x,y); Extract(cur->r,a,b,x,y);
    Up(cur);
  }

  inline Pt *New(int x,int y,int g){
    t->x=t->minx=t->maxx=x;
    t->y=t->miny=t->maxy=y;
    t->g=g; t->used=0; t->size=1;
    return t++;
  }

  Pt **Rb; int PP;

  inline int Size(Pt *cur){
    if(!cur) return 0; return cur->size;
  }

  void Insert(Pt *&cur,Pt *x,int p=1){
    if(!cur) {
      cur=x; return ;
    }
    if(p){
      if(x->x>cur->x) Insert(cur->r,x,p^1); else Insert(cur->l,x,p^1);
      Up(cur);
    }
    else{
      if(x->y>cur->y) Insert(cur->r,x,p^1); else Insert(cur->l,x,p^1);
      Up(cur);
    }
    if(Size(cur->l)*0.7>=Size(cur->r) || Size(cur->r)*0.7>=Size(cur->l)) Rb=&cur,PP=p;
  }

  Pt *r[N];
  int tt;

  void dfs1(Pt *cur){
    if(!cur) return ;
    if(!cur->used) r[++tt]=cur;
    dfs1(cur->l); dfs1(cur->r);
  }

  inline bool cmp(const Pt *a,const Pt *b){
    if(PP) return a->x<b->x;
    else return a->y<b->y;
  }

  Pt *Build(int L,int R){
    if(L>R) return 0;
    int mid=L+R>>1; Pt *ret;
    nth_element(r+L,r+mid,r+R+1,cmp);
    ret=r[mid]; PP^=1;
    ret->l=Build(L,mid-1);
    ret->r=Build(mid+1,R);
    Up(ret); return ret;
  }

  inline void rebuild(Pt *&cur){
    tt=0; dfs1(cur);
    for(int i=1;i<=tt;i++) r[i]->l=r[i]->r=0;
    cur=Build(1,tt);
  }

  inline void Insert(int x,int y,int g){
    Rb=0; Insert(root,New(x,y,g));
    if(Rb) rebuild(*Rb);
  }
}

int Gfat(int x){
  return x==fa[x]?x:fa[x]=Gfat(fa[x]);
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(q); KDT::t=KDT::pool;
  while(q--){
    int opt; read(opt);
    if(opt==1){
      n++; fa[n]=n; read(l[n]); read(r[n]);
      v.clear();
      KDT::Extract(KDT::root,-(1<<30),l[n]+1,l[n]-1,r[n]-1);
      KDT::Extract(KDT::root,l[n]+1,r[n]+1,r[n]-1,1<<30);
      KDT::Extract(KDT::root,-(1<<30),r[n],l[n],1<<30);
      for(int i=0;i<v.size();i++)
    fa[Gfat(v[i])]=n,l[n]=min(l[n],l[v[i]]),r[n]=max(r[n],r[v[i]]);
      KDT::Insert(l[n],r[n],n);
      //if(n%1000==0) KDT::rebuild(KDT::root);
      //KDT::Insert(KDT::root,l[n],r[n],n);
    }
    else{
      int x,y; read(x); read(y);
      x=Gfat(x); y=Gfat(y);
      if(x==y || (l[x]>l[y] && l[x]<r[y]) || (r[x]>l[y] && r[x]<r[y])) puts("YES");
      else puts("NO");
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值