题解 P2184 【贪婪大陆】

zhengrunzhe

2019-10-18 13:01:59

Solution

首先观察题目实质: 1.扔一个区间[l,r] 2.查询有多少个之前插入过的区间与[l,r]有交 回忆我们线段树如何判断当前节点代表的区间[l,r]与询问区间[L,R]没有交集 ```cpp if (l>R||r<L)return 0; ``` 没有交集是l>R||r<L那么有交集就是!(l>R||r<L)=l<=R&&r>=L 然后这就变成了一个二维偏序问题 $$r>=L.l<=R$$ (l,r代表之前插入过的所有区间,L,R代表查询区间) 然后这个玩意如果在线的话显然可以直接树套树直接爆搞 去cdq分治离线你就要加一维时间变成三维偏序非常不爽 什么两个树状数组线段树做法我太菜了不会 然后我就用了个$KDtree$维护这个二维偏序 要动态插入,替罪羊思想重构即可 ```cpp #include<cstdio> #include<algorithm> using std::nth_element; template<class type>inline const void read(type &in) { in=0;char ch=getchar();bool f=0; while (ch<48||ch>57){if (ch=='-')f=1;ch=getchar();} while (ch>47&&ch<58)in=(in<<3)+(in<<1)+(ch&15),ch=getchar(); if (f)in=-in; } template<class type>inline const void write(type out) { if (out>9)write(out/10); putchar(out%10+48); } template<class type>inline const void writeln(type out) { if (out<0)out=-out,putchar('-'); write(out); putchar('\n'); } template<class type>inline const type max(const type &a,const type &b) { return a>b?a:b; } template<class type>inline const type min(const type &a,const type &b) { return a<b?a:b; } const int N=1e5+10,inf=2147483647,K=2; int n,m; int f; struct point { int d[K]; inline point(const int &x=0,const int &y=0){d[0]=x;d[1]=y;} inline const bool operator<(const point &p)const { return d[f]<p.d[f]; } }; template<int k>class KD_Tree { private: static const double alpha=0.75; struct tree { int size; tree *son[2]; point range,mx,mn; inline const void pushup() { size=son[0]->size+1+son[1]->size; for (int i=0;i<k;i++) mn.d[i]=min(range.d[i],min(son[0]->mn.d[i],son[1]->mn.d[i])), mx.d[i]=max(range.d[i],max(son[0]->mx.d[i],son[1]->mx.d[i])); } inline const bool at(const point &lower,const point &upper) { for (int i=0;i<k;i++) if (!(range.d[i]>=lower.d[i]&&range.d[i]<=upper.d[i])) return 0; return 1; } inline const bool in(const point &lower,const point &upper) { for (int i=0;i<k;i++) if (!(mn.d[i]>=lower.d[i]&&mx.d[i]<=upper.d[i])) return 0; return 1; } inline const bool out(const point &lower,const point &upper) { for (int i=0;i<k;i++) if (mn.d[i]>upper.d[i]||mx.d[i]<lower.d[i]) return 1; return 0; } inline const bool unbalanced() { return son[0]->size>size*alpha||son[1]->size>size*alpha; } }*root,memory_pool[N],*tail,*null,*recycle[N]; int top,flag,cnt; point a[N]; inline const void init() { tail=memory_pool; null=tail++; root=null->son[0]=null->son[1]=null; for (int i=0;i<k;i++)null->mn.d[i]=inf,null->mx.d[i]=-inf; } inline tree *spawn(const point &x) { tree *p=top?recycle[--top]:tail++; p->size=1; p->range=p->mx=p->mn=x; p->son[0]=p->son[1]=null; return p; } inline const void travel(tree *p) { if (p==null)return; travel(p->son[0]); a[++cnt]=p->range; recycle[top++]=p; travel(p->son[1]); } inline tree *build(int l,int r,int d) { if (l>r)return null; int mid=l+r>>1;f=d; nth_element(a+l,a+mid,a+r+1); tree *p=spawn(a[mid]); if (l==r)return p; p->son[0]=build(l,mid-1,(d+1)%k); p->son[1]=build(mid+1,r,(d+1)%k); p->pushup(); return p; } inline const void rebuild(tree *&p,int d) { cnt=0; travel(p); p=build(1,cnt,d); } inline tree **insert(tree *&p,const point &x,int d) { if (p==null)return p=spawn(x),&null; tree **bad=insert(p->son[p->range.d[d]<x.d[d]],x,(d+1)%k); p->pushup(); if (p->unbalanced())bad=&p,flag=d; return bad; } inline const int query(tree *p,const point &x,const point &y) { if (p==null)return 0; if (p->out(x,y))return 0; if (p->in(x,y))return p->size; return p->at(x,y)+query(p->son[0],x,y)+query(p->son[1],x,y); } public: inline KD_Tree(){init();} inline const void insert(int x,int y) { tree **bad=insert(root,point(x,y),flag=0); if (*bad==null)return; rebuild(*bad,flag); } inline const int query(int x1,int y1,int x2,int y2) { return query(root,point(x1,y1),point(x2,y2)); } }; KD_Tree<K>kdt; int main() { read(n);read(m); for (int opt,l,r;m--;) if (read(opt),read(l),read(r),opt&1)kdt.insert(l,r); else writeln(kdt.query(-inf,l,r,inf)); return 0; } ```