zoukankan      html  css  js  c++  java
  • 「LOJ #6016」崂山白花蛇草水

    Description

    在一个 (n imes n) 的二维平面上,支持两种操作,操作共 (q) 组:

    • 加入一个 权值为 (v) 的二维点 ((x,y))
    • 在所有满足 (x_1 le xle x_2 ext{ and } y_1le yle y_2) 的点中,找出点权第 (k) 大的点,输出它的点权。或者判断是否存在。

    强制在线。

    Hint

    (1le nle 5 imes 10^5,1le qle 10^5)

    (1le vle 10^9,1le kle q)

    Solution

    题目有一个操作是求 ( ext{k-th}),那么很容易想到权值线段树,而且实现十分简单。

    下面是(动态开点)权值线段树 的 查找 ( ext{k-th}) 的代码:

    int sgtQueryKth(int k,int l=1,int r=INT_MAX,int rt=sgtRt) {
    	if(!rt) return -1;
    	if(l==r) return l;
    	int mid=(l+r)>>1;
    	if(size[rc[rt]]>=k)
    		return sgtQueryKth(k,mid+1,r,rc[rt]);
    	else
    		return sgtQueryKth(k-size[rc[rt]],l,mid,lc[rt]);
    }
    

    其中,size[rc[rt]]全局 中 结点 rc[rt] 所管辖值域中的 元素的个数

    然而现在我们有了限制,怎么办?我们需要快速计算 限定矩形范围 中 结点 rc[rt] 所管辖值域中的 元素的个数

    那么不难想到用 KD-Tree 来维护,实际上就是二维数点。再具体一点,就是 权值线段树的每个结点 都维护一个 KD-Tree,对于权值线段树上的结点 rt 那么 rt 上的 KDT 就维护着 结点 rt 所管辖的值域中的所有二维点

    所有,只需要再原来朴素的权值线段树的代码上,改一下……

    int sgtQueryKth(int xL,int xR,int yL,int yR,int k,int l=1,int r=V,int rt=sgtRt) {
    	if(l==r) return l;
    	int insideCnt=kdtCount(xL,xR,yL,yR,kdtRt[rc[rt]]); // 在当前结点的右儿子的 KDT 上做二维数点。
    	if(insideCnt>=k)//之后的所有原来的 size[rc[rt]] 都换为 insideCnt
    		return sgtQueryKth(xL,xR,yL,yR,k,mid+1,r,rc[rt]);
    	else
    		return sgtQueryKth(xL,xR,yL,yR,k-insideCnt,l,mid,lc[rt]);
    }
    

    emm……基本没变??

    是的。

    那么插入的操作也可以很快写出来了:

    void sgtUpdate(int *dat,int v,int l=1,int r=V,int &rt=sgtRt) {//dat[0] => x坐标,dat[1] => y坐标
    	if(!rt) rt=++sgtNCnt;
    	kdtInsert(dat,kdtRt[rt]);//原本为 size[rt]+=v ,现换成 KDT 的插入
    	if(l==r) return;
    	if(v<=mid) sgtUpdate(dat,v,l,mid,lc[rt]);
    	else sgtUpdate(dat,v,mid+1,r,rc[rt]);
            //不要直接 pushup,随便想想就知道那样子复杂度会爆炸,应使用 Path-update 的方式。
    }
    

    那么,这就是传说中的树套树中的 权值线段树 套 KD-Tree 了!

    【镇楼】

    权值线段树 套 KDT 的图示:

    GwIDI0.png

    GwIBaq.png

    GwI0Zn.png

    Code

    完整代码(O2)

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : LOJ #6016 崂山白花蛇草水
     */
    #include<cstdio>
    #include<algorithm>
    #include<utility>
    using namespace std;
    
    namespace fastIO_int {
    	int get_int() {
    		int x=0,f=1;char c=getchar();
    		while(c<'0'||c>'9')	{
    			if(c=='-')f=-1;
    			c=getchar();
    		}
    		while(c>='0'&&c<='9')
    			x=x*10+c-'0',c=getchar();
    		return f*x;
    	}
    	
    	void read(){}
    	template<class T1,class ...T2>
    	void read(T1 &i,T2&... rest) {
    		i=get_int();
    		read(rest...);
    	}
    	
    	void put_int(int x) {
    		if(x<0)putchar('-'),x=-x;
    		if(x>9)put_int(x/10);
    		putchar(x%10+'0');
    	}
    	
    	void write(){}
    	template<class T1,class ...T2>
    	void write(T1 i,T2... rest) {
    		put_int(i),putchar(' ');
    		write(rest...);
    	}
    };
    
    const int V=1e9;
    const int N=1e5+5;
    #define lc(x) tree[x].lc
    #define rc(x) tree[x].rc
    struct Node {
    	int lc,rc,size;
    	int dat[2];
    	int Max[2],Min[2];
    	Node() {
    		lc=rc=size=0;
    		dat[0]=dat[1]=0;
    		Max[0]=Max[1]=Min[0]=Min[1]=0;
    	}
    	Node(int x,int y) {
    		lc=rc=0,size=1;
    		dat[0]=x,dat[1]=y;
    		Max[0]=Min[0]=x,Max[1]=Min[1]=y;
    	}
    	inline int& operator [] (const int &t) {
    		return dat[t];
    	}
    }tree[N<<6];
    int kdtNCnt;
    inline void maintain(int rt) {
    	for(register int i=0;i<2;i++) {
    		tree[rt].Max[i]=tree[rt].Min[i]=tree[rt][i];
    		if(lc(rt)) {
    			tree[rt].Max[i]=max(tree[rt].Max[i],tree[lc(rt)].Max[i]);
    			tree[rt].Min[i]=min(tree[rt].Min[i],tree[lc(rt)].Min[i]);
    		}
    		if(rc(rt)) {
    			tree[rt].Max[i]=max(tree[rt].Max[i],tree[rc(rt)].Max[i]);
    			tree[rt].Min[i]=min(tree[rt].Min[i],tree[rc(rt)].Min[i]);
    		}
    	}
    	tree[rt].size=tree[lc(rt)].size+tree[rc(rt)].size+1;
    }
    namespace kdtRebuildPart {
    	#define alpha 0.75
    	pair<int,int> pnt[N];
    	int stk[N],top=0;
    	struct comparator {
    		int dim;
    		inline bool operator () (const pair<int,int> &x,const pair<int,int> &y) const {
    			if(dim==0) return x.first < y.first;
    			else return x.second < y.second;
    		}
    	}cmp;
    	int build(int l,int r,int d) {
    		if(l>r) return 0;
    		int rt=stk[top--], mid=(l+r)>>1;
    		cmp.dim=d;
    		nth_element(pnt+l,pnt+mid,pnt+r+1,cmp);
    		tree[rt]=Node(pnt[mid].first,pnt[mid].second);
    		lc(rt)=build(l,mid-1,d^1);
    		rc(rt)=build(mid+1,r,d^1);
    		return maintain(rt),rt;
    	}
    	void destroy(int rt) {
    		if(!rt) return;
    		stk[++top]=rt;
    		pnt[top]=make_pair(tree[rt][0],tree[rt][1]);
    		destroy(lc(rt));
    		destroy(rc(rt));
    	}
    	inline bool isBalanced(int rt) {
    		if(tree[rt].size*alpha<tree[lc(rt)].size*1.0) return false;
    		if(tree[rt].size*alpha<tree[rc(rt)].size*1.0) return false;
    		return true;
    	}
    	inline void rebuild(int &rt,int d) {
    		if(isBalanced(rt)) return;
    		top=0,destroy(rt);
    		rt=build(1,top,d);
    	}
    	#undef alpha
    }
    void kdtInsert(int *dat,int &rt,int d=0) {
    	if(!rt) {
    		rt=++kdtNCnt;
    		tree[rt]=Node(dat[0],dat[1]);
    		return;
    	}
    	if(dat[d]<=tree[rt][d])
    		kdtInsert(dat,lc(rt),d^1);
    	else
    		kdtInsert(dat,rc(rt),d^1);
    	maintain(rt);
    	kdtRebuildPart::rebuild(rt,d);
    }
    inline bool allIn(int xL,int xR,int yL,int yR,int rt) {
    	return (tree[rt].Max[0]<=xR&&tree[rt].Max[1]<=yR&&tree[rt].Min[0]>=xL&&tree[rt].Min[1]>=yL);
    }
    inline bool allOut(int xL,int xR,int yL,int yR,int rt) {
    	return (tree[rt].Max[0]<xL||tree[rt].Max[1]<yL||tree[rt].Min[0]>xR||tree[rt].Min[1]>yR);
    }
    inline int checkIn(int xL,int xR,int yL,int yR,int rt) {
    	return !(tree[rt][0]<xL||tree[rt][0]>xR||tree[rt][1]<yL||tree[rt][1]>yR);
    }
    int kdtCount(int xL,int xR,int yL,int yR,int rt) {
    	if(!rt) return 0;
    	if(allIn(xL,xR,yL,yR,rt)) return tree[rt].size;
    	if(allOut(xL,xR,yL,yR,rt)) return 0;
    	int ret=checkIn(xL,xR,yL,yR,rt);
    	return ret+kdtCount(xL,xR,yL,yR,lc(rt))+kdtCount(xL,xR,yL,yR,rc(rt));
    }
    #undef lc
    #undef rc
    
    #define mid ((l+r)>>1)
    int kdtRt[N<<4];
    int lc[N<<4],rc[N<<4];
    int sgtNCnt=0;
    int sgtRt=0;
    
    void sgtUpdate(int *dat,int v,int l=1,int r=V,int &rt=sgtRt) {
    	if(!rt) rt=++sgtNCnt;
    	kdtInsert(dat,kdtRt[rt]);
    	if(l==r) return;
    	if(v<=mid) sgtUpdate(dat,v,l,mid,lc[rt]);
    	else sgtUpdate(dat,v,mid+1,r,rc[rt]);
    }
    int sgtQueryKth(int xL,int xR,int yL,int yR,int k,int l=1,int r=V,int &rt=sgtRt) {
    	if(l==r) return l;
    	int insideCnt=kdtCount(xL,xR,yL,yR,kdtRt[rc[rt]]);
    	if(insideCnt>=k)
    		return sgtQueryKth(xL,xR,yL,yR,k,mid+1,r,rc[rt]);
    	else
    		return sgtQueryKth(xL,xR,yL,yR,k-insideCnt,l,mid,lc[rt]);
    }
    #undef mid
    
    int n,q;
    signed main() {
    	using fastIO_int::read;
    	using fastIO_int::write;
    	read(n,q);
    	for(int last=0;q;--q) {
    		int cmd; read(cmd);
    		if(cmd==1) {
    			int dat[2],v;
    			read(dat[0],dat[1],v);
    			dat[0]^=last;
    			dat[1]^=last;
    			v^=last;
    			sgtUpdate(dat,v);
    		} else {
    			int xL,yL,xR,yR,k;
    			read(xL,yL,xR,yR,k);
    			xL^=last,xR^=last;
    			yL^=last,yR^=last;
    			k^=last;
    			if(kdtCount(xL,xR,yL,yR,kdtRt[sgtRt])<k) {
    				last=0;
    				puts("NAIVE!ORZzyz.");
    			} else {
    				last=sgtQueryKth(xL,xR,yL,yR,k);
    				write(last),putchar('
    ');
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    数据库性能优化之冗余字段的作用
    SQL里面的排序语句desc和ASC有什么区别
    Mybatis@options注解属性useGeneratedKeys,keyProperty,keyColumn的使用
    关于resultType与parameterType的基本使用和区别
    阿里云Centos7的部署springboot后mysql中文问号乱码
    LINUX下启动/停止/重启MYSQL
    CondenseNet:可学习分组卷积,原作对DenseNet的轻量化改造 | CVPR 2018
    MnasNet:经典轻量级神经网络搜索方法 | CVPR 2019
    MobileNetV1/V2/V3简述 | 轻量级网络
    ShuffleNetV1/V2简述 | 轻量级网络
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/12632342.html
Copyright © 2011-2022 走看看