• 洛谷P4848 崂山白花蛇草水 权值线段树+KDtree


    题目描述

    神犇 (Aleph)(SDOI Round2) 前立了一个 (flag):如果进了省队,就现场直播喝崂山白花蛇草水。凭借着神犇 (Aleph) 的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了。蒟蒻 (Bob) 特地为他准备了 (999,999,999,999,999,999) 瓶崂山白花蛇草水,想要灌神犇 (Aleph)。神犇 (Aleph) 求(跪着的)蒟蒻 (Bob) 不要灌他,由于神犇 (Aleph) 是神犇,蒟蒻 (Bob) 最终答应了他的请求,但蒟蒻 (Bob) 决定将计就计,也让神犇 $Aleph4 回答一些问题。

    具体说来,蒟蒻 (Bob) 会在一个宽敞的广场上放置一些崂山白花蛇草水(可视为二维平面上的一些整点),然后询问神犇 (Aleph) 在矩形区域 (x_1le xle x_2,y_1le yle y_2) 中,崂山白花蛇草水瓶数第 (k) 多的是多少。为了避免麻烦,蒟蒻 (Bob) 不会在同一个位置放置两次或两次以上的崂山白花蛇草水,但蒟蒻 (Bob) 想为难一下神犇 (Aleph),希望他能在每次询问时立刻回 答出答案。

    神犇 (Aleph) 不屑于做这种问题,所以把这个问题交给了你。

    输入格式

    输入的第一行为两个正整数 (n)(q),表示横纵坐标的范围和蒟蒻 (Bob) 的操作次数(包括放置次数和询问次数)。

    接下来 (q) 行,每行代表蒟蒻 (Bob) 的一个操作,操作格式如下:
    首先第一个数字 (mathrm{type}),表示操作种类。(mathrm{type}=1) 表示放置,(mathrm{type}=2) 表示询问。
    (mathrm{type}=1),接下来会有三个正整数 (x, y, v),表示在坐标整点 ((x, y)) 放置v瓶崂山白花蛇草水。
    (mathrm{type}=2),接下来会有五个正整数 (x_1, y_1, x_2, y_2, k),表示询问矩形区域 (x_1le xle x_2,y_1le yle y_2)​ 中,崂山白花蛇草水瓶数第 (k) 多的是多少。

    为了体现程序的在线性,你需要将每次读入的数据(除了 (mathrm{type}) 值)都异或 (mathrm{lastans}),其中 (mathrm{lastans}) 表示上次询问的答 案。如果上次询问的答案为 (NAIVE!ORZzyz).(见样例输出),则将 (mathrm{lastans}) 置为 (0)。初始时的 (mathrm{lastans})(0)。 初始时平面上不存在崂山白花蛇草水。

    输出格式

    对于每个 (mathrm{type}=2) 的操作,一行输出崂山白花蛇草水瓶数第 (k) 多的是多少。若不存在第 (k) 多的瓶数, 请输出 (NAIVE!ORZzyz.)

    输入输出样例

    输入 #1

    10 7
    1 1 1 1
    1 2 2 3
    1 4 1 2
    1 3 4 4
    2 1 1 4 1 3
    2 2 2 3 5 4
    2 2 1 4 4 2
    

    输出 #1

    NAIVE!ORZzyz.
    NAIVE!ORZzyz.
    3
    

    说明/提示

    对于所有数据,(nle500000)(qle100000)(1le x, yle n)(1le vle 10^9)(1le x_1le x_2le n)(1le y_1le y_2le n)(1le kle q)

    分析

    在给定的矩形内查询第 (k) 大的权值

    动态加点,强制在线

    这种题一个非常好想的做法就是开一个小根堆维护最大的 (k) 个元素

    每次只更新队首的元素

    这种做法在 (k) 比较小的情况下是没有问题的

    然而这道题 (k) 的范围达到了 (10^5) ,显然会 (T) 到飞起

    所以我们需要另一种能够支持查询区间 (k) 大值的数据结构,比如权值线段树

    权值线段树的每个节点上都维护一棵 (kdtree)

    (kdtree) 中存储权值为当前节点下标的所有点的坐标

    因为要动态加点,为了防止二叉树退化成一条链,需要借鉴替罪羊的思想暴力重构

    要注意的是,暴力重构时,不能这样写

    void dfs(rg int da){
    	sta[++tp]=da;
    	jl[tp]=ktr[da];
    	if(ktr[da].lc) dfs(ktr[da].lc);
    	if(ktr[da].rc) dfs(ktr[da].rc);
    }
    

    而要这样写

    void dfs(rg int da){
    	sta[++tp]=da;
    	jl[tp].d[0]=ktr[da].d[0];
    	jl[tp].d[1]=ktr[da].d[1];
    	if(ktr[da].lc) dfs(ktr[da].lc);
    	if(ktr[da].rc) dfs(ktr[da].rc);
    }
    

    因为你直接继承 (min)(max) 会对之后节点的更新造成影响

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    const int maxn=1e6+5,INF=1e9;
    const double alpha=0.75;
    inline int Max(rg int aa,rg int bb){
    	return aa>bb?aa:bb;
    }
    inline int Min(rg int aa,rg int bb){
    	return aa<bb?aa:bb;
    }
    int orz,kcnt,sta[maxn],tp;
    struct kdt{
    	int lc,rc,mn[2],mx[2],d[2],siz;
    	friend bool operator <(const kdt& A,const kdt& B){
    		return A.d[orz]<B.d[orz];
    	}
    }ktr[maxn*5],jl[maxn*5];
    inline int newnode(){
    	if(tp) return sta[tp--];
    	else return ++kcnt;
    }
    void push_up(rg int da){
    	rg int lc=ktr[da].lc,rc=ktr[da].rc;
    	for(rg int i=0;i<2;i++){
    		ktr[da].mn[i]=ktr[da].mx[i]=ktr[da].d[i];
    		if(lc){
    			ktr[da].mn[i]=Min(ktr[da].mn[i],ktr[lc].mn[i]);
    			ktr[da].mx[i]=Max(ktr[da].mx[i],ktr[lc].mx[i]);
    		}
    		if(rc){
    			ktr[da].mn[i]=Min(ktr[da].mn[i],ktr[rc].mn[i]);
    			ktr[da].mx[i]=Max(ktr[da].mx[i],ktr[rc].mx[i]);
    		}
    	}
    	ktr[da].siz=ktr[lc].siz+ktr[rc].siz+1;
    }
    int build(rg int l,rg int r,rg int pl){
    	orz=pl;
    	rg int da=newnode(),mids=(l+r)>>1;
    	std::nth_element(jl+l,jl+mids,jl+r+1);
    	ktr[da]=jl[mids];
    	if(l<mids) ktr[da].lc=build(l,mids-1,!pl);
    	if(mids<r) ktr[da].rc=build(mids+1,r,!pl);
    	push_up(da);
    	return da;
    }
    void dfs(rg int da){
    	sta[++tp]=da;
    	jl[tp].d[0]=ktr[da].d[0];
    	jl[tp].d[1]=ktr[da].d[1];
    	if(ktr[da].lc) dfs(ktr[da].lc);
    	if(ktr[da].rc) dfs(ktr[da].rc);
    }
    void check(rg int &da,rg int pl){
    	rg int lc=ktr[da].lc,rc=ktr[da].rc;
    	if(ktr[lc].siz>ktr[da].siz*alpha || ktr[rc].siz>ktr[da].siz*alpha){
    		tp=0;
    		dfs(da);
    		da=build(1,tp,pl);
    	}
    }
    int ad(rg int da,rg int nx,rg int ny,rg int pl){
    	if(!da){
    		da=newnode();
    		ktr[da].mn[0]=ktr[da].mx[0]=ktr[da].d[0]=nx;
    		ktr[da].mn[1]=ktr[da].mx[1]=ktr[da].d[1]=ny;
    		ktr[da].siz=1;
    		return da;
    	}
    	if(pl==0){
    		if(nx<ktr[da].d[0]) ktr[da].lc=ad(ktr[da].lc,nx,ny,!pl);
    		else ktr[da].rc=ad(ktr[da].rc,nx,ny,!pl);
    	} else {
    		if(ny<ktr[da].d[1]) ktr[da].lc=ad(ktr[da].lc,nx,ny,!pl);
    		else ktr[da].rc=ad(ktr[da].rc,nx,ny,!pl);
    	}
    	push_up(da);
    	if(ktr[da].siz>6) check(da,pl);
    	return da;
    }
    int cx(rg int da,rg int a1,rg int a2,rg int b1,rg int b2){
    	if(!da) return 0;
    	if(ktr[da].mx[0]<a1 || ktr[da].mn[0]>a2 || ktr[da].mx[1]<b1 || ktr[da].mn[1]>b2) return 0;
    	if(ktr[da].mx[0]<=a2 && ktr[da].mn[0]>=a1 && ktr[da].mx[1]<=b2 && ktr[da].mn[1]>=b1){
    		return ktr[da].siz;
    	}
    	rg int nans=0;
    	if(ktr[da].d[0]<=a2 && ktr[da].d[0]>=a1 && ktr[da].d[1]<=b2 && ktr[da].d[1]>=b1)  nans++;
    	nans+=cx(ktr[da].lc,a1,a2,b1,b2);
    	nans+=cx(ktr[da].rc,a1,a2,b1,b2);
    	return nans;
    }
    struct trr{
    	int lc,rc;
    }tr[maxn*10];
    int rk[maxn],cnt,rt;
    int xg(rg int da,rg int l,rg int r,rg int wz,rg int nx,rg int ny){
    	if(!da) da=++cnt;
    	rg int mids=(l+r)>>1;
    	rk[da]=ad(rk[da],nx,ny,0);
    	if(l==r) return da;
    	if(wz<=mids) tr[da].lc=xg(tr[da].lc,l,mids,wz,nx,ny);
    	else tr[da].rc=xg(tr[da].rc,mids+1,r,wz,nx,ny);
    	return da;
    }
    int trcx(rg int da,rg int l,rg int r,rg int kth,rg int a1,rg int a2,rg int b1,rg int b2){
    	if(l==r) return l;
    	rg int nans=cx(rk[tr[da].rc],a1,a2,b1,b2),mids=(l+r)>>1;
    	if(nans>=kth) return trcx(tr[da].rc,mids+1,r,kth,a1,a2,b1,b2);
    	else return trcx(tr[da].lc,l,mids,kth-nans,a1,a2,b1,b2);
    }
    int n,q,latans;
    int main(){
    	n=read(),q=read();
    	rg int aa,bb,cc,dd,ee,ff;
    	for(rg int i=1;i<=q;i++){
    		aa=read(),bb=read(),cc=read(),dd=read();
    		bb^=latans,cc^=latans,dd^=latans;
    		if(aa==1){
    			rt=xg(rt,1,INF,dd,bb,cc);
    		} else {
    			ee=read(),ff=read();
    			ee^=latans,ff^=latans;
    			if(cx(rk[rt],bb,dd,cc,ee)<ff){
    				printf("NAIVE!ORZzyz.
    ");
    				latans=0;
    			} else {
    				printf("%d
    ",latans=trcx(rt,1,INF,ff,bb,dd,cc,ee));
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    easyui datagrid 前后台代码
    JVM
    序列化
    Android UI设计
    多线程
    泛型
    字符串
    B+树:MySql数据库索引是如何实现的
    大数据判存算法:海量数据中快速判断某个数据是否存在
    陌生单词
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14247580.html
Copyright © 2020-2023  润新知