• KD-Tree


    KD-Tree

    写在KD-Tree讲解之前,请先让我评论一番,“这什么垃圾算法!这也太垃圾了!”BY Winniechen

    BY GXZlegend KD-Tree,时间复杂度可证的可以被可持久化线段树替代,时间复杂度不可证的...时间复杂度不可证时间复杂度就是能被卡成n^2

    但是,就算KD-Tree的时间复杂度是n^2,(卡起来很费劲,基本卡不到...)

    怎么说呢,就是n^2的暴搜加剪枝...至于剪枝...(学过暴搜就会的那种...每道题不同,但是相差不大,基本就是设一个估价函数之后瞎写)

    而有些剪枝之后的时间复杂度可证,其他不可证的就是没有剪枝那种。

    另外,KD-Tree主要难卡的地方是每次将一个区间横着切一刀,之后再纵向切一刀,反正就是各种找中位数之后劈成两半,按照平衡树的存储方式存储,至于树高太高一般的解决方法是暴力重构(发现树高大于一个值之后重新建树...),或者是替罪羊树的重构方式(我不会...)

    另外,KD-Tree不一定要按照010101的顺序切...(随机数什么的不也可以嘛...但是我没有写过,目测可行,而且更加卡不掉了...)

    这种高级操作一般不会用到,用到更多的是插入导致深度过深之后的重构。

    我们先讲一下KD-Tree的建树,每次以一维取中位数,作为这个节点,之后再分别建立左右子树。

    中位数不会求?排序nlogn,总时间复杂度:O(nlog^2nK)。等等?只需要中位数?快排啊!O(n)取中位数。算了,好好说话,nth_element(tr+l,tr+m,tr+r+1,cmp);就张这个样子就可以了。具体实现是快排...

    插入操作:BST的插入方式...我就不多说了...每次和这个节点的对应d进行比较之后排序就可以了。

    查询操作:暴搜+剪枝,我不说了...遍历树+剪枝...

    重构操作:找个随便的参数重构即可...(就是再建一遍树)

    例题时间:

    BZOJ2648: SJY摆棋子 & BZOJ2716: [Violet 3]天使玩偶

    分析:KD-Tree入门题...查询曼哈顿距离最小,设一个估价函数(建议手画一下,推一推,也不是很难理解),时间复杂度严格O(nsqrt(n)),跑得比CDQ分治+树状数组快了不少...毕竟每层四个树状数组看的我头皮发麻...

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define N 1000005
    #define inf 0x3f3f3f3f
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    char buf[100000],*p1,*p2;
    inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
    inline int rd() {
    	register int x=0;register char ch=nc();
    	while(ch<'0'||ch>'9') ch=nc();
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x;
    }
    int rot,d,ans,n,Q;
    struct KDTree
    {
    	int ch[2],maxn[2],minn[2],p[2];
    	friend bool operator<(const KDTree &a,const KDTree &b)
    	{
    		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
    	}
    }tr[N];
    #define PushUp(rt,s) tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]),tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]),tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]),tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1])
    int build(int l,int r,int flag)
    {
    	int m=(l+r)>>1;
    	d=flag,nth_element(tr+l,tr+m,tr+r+1);
    	tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0];
    	tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1];
    	if(l<m)tr[m].ch[0]=build(l,m-1,flag^1),PushUp(m,tr[m].ch[0]);
    	if(m<r)tr[m].ch[1]=build(m+1,r,flag^1),PushUp(m,tr[m].ch[1]);
    	return m;
    }
    inline void insert(int x)
    {
    	int *rt=&rot;d=0;int tmp=0;
    	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1,tmp++;
    	*rt=x;
    	if(tmp>=200)rot=build(1,n,0);
    }
    #define get_dis(rt,x,y) (max(tr[rt].minn[0]-x,0)+max(x-tr[rt].maxn[0],0)+max(tr[rt].minn[1]-y,0)+max(y-tr[rt].maxn[1],0))
    void query(int rt,int x,int y)
    {
    	int dis_rt=abs(x-tr[rt].p[0])+abs(y-tr[rt].p[1]);
    	int dis_ls=ls?get_dis(ls,x,y):inf,dis_rs=rs?get_dis(rs,x,y):inf;
    	ans=min(ans,dis_rt);
    	if(dis_ls<dis_rs)
    	{
    		if(dis_ls<ans)query(ls,x,y);
    		if(dis_rs<ans)query(rs,x,y);
    	}else
    	{
    		if(dis_rs<ans)query(rs,x,y);
    		if(dis_ls<ans)query(ls,x,y);
    	}
    }
    int main()
    {
    	n=rd();Q=rd();
    	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
    	while(Q--)
    	{
    		int op=rd(),x=rd(),y=rd();
    		if(op==1)n++,tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x,tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y,insert(n);
    		else ans=inf,query(rot,x,y),printf("%d
    ",ans);
    	}return 0;
    }

    BZOJ4066: 简单题

    分析:如果不强制在线的话CDQ分治搞一搞很可以...但是显然,它强制在线...和上一题一样,时间复杂度可证的O(nsqrt(n)),具体证明我就不写了...查询的时候,类似Splay区间操作的存储方式,存一个子树和即可。

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <cstdlib>
    #include <cstring>
    using namespace std;
    #define N 200005
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    int rot,d,last,n,Q;
    struct KDTree
    {
    	int p[2],ch[2],maxn[2],minn[2],sum,w;
    	friend bool operator<(const KDTree &a,const KDTree &b)
    	{
    		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
    	}
    }tr[N];
    void PushUp(int rt,int s)
    {
    	tr[rt].sum+=tr[s].sum;
    	tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]);
    	tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1]);
    	tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]);
    	tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]);
    }
    int build(int l,int r,int flag)
    {
    	int m=(l+r)>>1,rt=m;d=flag;nth_element(tr+l,tr+m,tr+r+1);tr[m].ch[0]=tr[m].ch[1]=0;
    	tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0];tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1];tr[m].sum=tr[m].w;
    	if(l<m)ls=build(l,m-1,!flag),PushUp(m,ls);if(m<r)rs=build(m+1,r,!flag),PushUp(m,rs);return m;
    }
    void insert(int x)
    {
    	int *rt=&rot;d=0;
    	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1;
    	*rt=x;
    }
    int query(int rt,int x1,int y1,int x2,int y2)
    {
    	if(!rt||tr[rt].maxn[0]<x1||tr[rt].maxn[1]<y1||tr[rt].minn[0]>x2||tr[rt].minn[1]>y2)return 0;
    	if(tr[rt].maxn[0]<=x2&&tr[rt].maxn[1]<=y2&&tr[rt].minn[0]>=x1&&tr[rt].minn[1]>=y1)return tr[rt].sum;
    	int ret=0;if(tr[rt].p[0]>=x1&&tr[rt].p[1]>=y1&&tr[rt].p[0]<=x2&&tr[rt].p[1]<=y2)ret+=tr[rt].w;
    	ret+=query(ls,x1,y1,x2,y2)+query(rs,x1,y1,x2,y2);return ret;
    }
    int main()
    {
    	scanf("%*d");
    	while(1)
    	{
    		int op,x,y,z,w;scanf("%d",&op);if(op==3)break;
    		scanf("%d%d%d",&x,&y,&z);x^=last,y^=last,z^=last;
    		if(op==1)
    		{
    			tr[++n].sum=tr[n].w=z;
    			tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x;
    			tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y;
    			insert(n);if(n%10000==0)rot=build(1,n,0);
    		}
    		else scanf("%d",&w),w^=last,printf("%d
    ",last=query(rot,x,y,z,w));
    	}return 0;
    }
    

    23333,跑不过CDQ分治...

    BZOJ1941: [Sdoi2010]Hide and Seek

    分析:KD-Tree基础题,两个估价函数,分别对应曼哈顿距离最大和最小

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    #include <cstring>
    #include <cstdlib>
    #include <bitset>
    using namespace std;
    #define N 500005
    #define inf 2147483647
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]
    int rot,d,n,ans,ans1,ans2;
    char buf[100000],*p1,*p2;
    __attribute__((optimize("-O3")))inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
    __attribute__((optimize("-O3")))inline int rd() {
    	register int x=0;register char ch=nc();
    	while(ch<'0'||ch>'9') ch=nc();
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x;
    }
    struct KDTree{int ch[2],p[2],mx[2],mn[2];
    	friend bool operator<(const KDTree &a,const KDTree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];}}tr[N];
    #define PushUp(rt,s) tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1])
    __attribute__((optimize("-O3")))int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
    	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
    }
    #define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0))
    #define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0))
    __attribute__((optimize("-O3")))void query_min(int rt,int x,int y)
    {
    	int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_min(ls,x,y):inf,dis_rs=rs?get_dis_min(rs,x,y):inf;if(dis_rt)ans1=min(dis_rt,ans1);
    	if(dis_ls<dis_rs){if(dis_ls<ans1)query_min(ls,x,y);if(dis_rs<ans1)query_min(rs,x,y);}else{if(dis_rs<ans1)query_min(rs,x,y);if(dis_ls<ans1)query_min(ls,x,y);}
    }
    __attribute__((optimize("-O3")))void query_max(int rt,int x,int y)
    {
    	int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_max(ls,x,y):-inf,dis_rs=rs?get_dis_max(rs,x,y):-inf;ans2=max(dis_rt,ans2);
    	if(dis_ls>dis_rs){if(dis_ls>ans2)query_max(ls,x,y);if(dis_rs>ans2)query_max(rs,x,y);}else{if(dis_rs>ans2)query_max(rs,x,y);if(dis_ls>ans2)query_max(ls,x,y);}
    }
    __attribute__((optimize("-O3")))int main()
    {
    	n=rd();ans=inf;
    	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
    	for(int i=1;i<=n;i++){ans1=inf,ans2=-inf;query_min(rot,tr[i].p[0],tr[i].p[1]);query_max(rot,tr[i].p[0],tr[i].p[1]);ans=min(ans2-ans1,ans);}
    	printf("%d
    ",ans);return 0;
    }

    BZOJ2850: 巧克力王国

    分析:说实话,这题KD-Tree的时间复杂度是可证的n^2...正解给的是似乎是半平面交+整体二分,显然我并不会...GXZlegend出的数据卡的我头皮发麻...不过,数据这么水就当是练一练KD-Tree了。

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    #include <cstring>
    #include <cstdlib>
    #include <bitset>
    using namespace std;
    #define N 50005
    #define ll long long
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) tr[rt].sum=tr[rt].w,ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]
    int d,rot,n,Q;ll a,b,c;
    struct KDTree
    {
    	int mx[2],p[2],ch[2],mn[2],w;ll sum;
    	friend bool operator<(const KDTree &a,const KDTree &b)
    	{
    		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
    	}
    }tr[N];
    #define PushUp(rt,s) tr[rt].sum+=tr[s].sum,tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1])
    int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
    	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
    }
    #define check(rt) (((a*tr[rt].mx[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mx[0]+b*tr[rt].mn[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mn[1])<c))
    ll query(int rt)
    {
    	int tmp;if(!rt||!(tmp=check(rt)))return 0;if(tmp==4)return tr[rt].sum;
    	ll ret=0;if(((ll)tr[rt].p[0]*a+(ll)tr[rt].p[1]*b)<c)ret+=tr[rt].w;
    	return ret+query(ls)+query(rs);
    }
    int main()
    {
    	scanf("%d%d",&n,&Q);
    	for(int i=1;i<=n;i++)scanf("%d%d%d",&tr[i].p[0],&tr[i].p[1],&tr[i].w);rot=build(1,n,0);
    	while(Q--)
    	{
    		scanf("%lld%lld%lld",&a,&b,&c);
    		printf("%lld
    ",query(rot));
    	}return 0;
    }

    BZOJ3489: A simple rmq problem

    分析:这题模型很好,考虑如果存在是什么情况,也就是上一次出现在l左边,下一次出现在r右边,那么将每一个a[i]建成一个三维的点,之后查询一个长方体内的点数,这题时间复杂度还是可证的...当然,如果不强制在线的话,我会考虑CDQ分治什么的...

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    #include <cstring>
    #include <cstdlib>
    #include <bitset>
    using namespace std;
    #define N 100005
    #define inf 2147483647
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i];
    int rot,d,n,Q,ans,p[N],nxt[N],pre[N],a[N];
    char buf[100000],*p1,*p2;
    inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
    inline int rd() {
    	register int x=0;register char ch=nc();
    	while(ch<'0'||ch>'9') ch=nc();
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x;
    }
    struct KDTree
    {
    	int p[3],ch[2],mx[3],mn[3],maxx,w;
    	friend bool operator<(const KDTree &a,const KDTree &b)
    	{
    		return a.p[d]==b.p[d]?(a.p[(d+1)%3]==b.p[(d+1)%3]?a.p[(d+2)%3]<b.p[(d+2)%3]:a.p[(d+1)%3]<b.p[(d+1)%3]):a.p[d]<b.p[d];
    	}
    }tr[N];
    void PushUp(int rt,int s)
    {
    	for(int i=0;i<3;i++)
    	{
    		tr[rt].mx[i]=max(tr[rt].mx[i],tr[s].mx[i]);
    		tr[rt].mn[i]=min(tr[rt].mn[i],tr[s].mn[i]);
    	}tr[rt].maxx=max(tr[rt].maxx,tr[s].maxx);
    } 
    int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i];
    	if(rt>l)ls=build(l,rt-1,(flag+1)%3),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,(flag+1)%3),PushUp(rt,rs);return rt;
    }
    int check(int rt,int x,int y)
    {
    	if(tr[rt].maxx<=ans||tr[rt].mx[0]<x||tr[rt].mn[0]>y||tr[rt].mn[1]>=x||tr[rt].mx[2]<=y)return 0;
    	return 1;
    }
    void query(int rt,int x,int y)
    {
    	if(!rt||!check(rt,x,y))return ;
    	if(tr[rt].p[0]>=x&&tr[rt].p[0]<=y&&tr[rt].p[1]<x&&tr[rt].p[2]>y)ans=max(ans,tr[rt].w);
    	query(ls,x,y);query(rs,x,y);
    }
    int main()
    {
    	n=rd();Q=rd();
    	for(int i=1,x;i<=n;i++)
    	{
    		x=rd();pre[i]=p[x];a[i]=x;
    		if(p[x])nxt[p[x]]=i;
    		p[x]=i;
    	}
    	for(int i=1;i<=n;i++)if(!nxt[i])nxt[i]=n+1;
    	for(int i=1;i<=n;i++)tr[i].p[0]=i,tr[i].p[1]=pre[i],tr[i].p[2]=nxt[i],tr[i].w=tr[i].maxx=a[i];
    	rot=build(1,n,0);
    	while(Q--)
    	{
    		int x=rd(),y=rd();
    		x=(x+ans)%n+1,y=(y+ans)%n+1;
    		if(x>y)swap(x,y);
    		ans=0;query(rot,x,y);
    		printf("%d
    ",ans);
    	}return 0;
    }

    BZOJ2989: 数列 & BZOJ4170: 极光

    分析:目测需要旋转坐标系...毕竟一个斜正方形的查询时间复杂度是不可证的,据说是O(n^2)的,但是呢,我就试了试,AC快乐!查询类似线段树的查询方式和4066一样,反正就是那么一个姿势...

    附上代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    #define N 100005
    #define inf 0x3f3f3f3f
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1],tr[rt].sum=tr[rt].w;
    char buf[1000000],*p1,*p2;
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
    __attribute__((optimize("-O3")))inline int rd() {
    	register int x=0;register char ch=nc();
    	while(ch<'0'||ch>'9') ch=nc();
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x;
    }
    int n,d,Q,k,a[N],rot;
    struct KDtree
    {
    	int ch[2],mx[2],mn[2],p[2],sum,w;
    	friend bool operator<(const KDtree &a,const KDtree &b)
    	{
    		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
    	}
    }tr[N];
    void PushUp(int rt,int s)
    {
    	tr[rt].sum+=tr[s].sum;
    	tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]);
    	tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]);
    	tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]);
    	tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]);
    }
    int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
    	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
    }
    void insert(int x)
    {
    	int *rt=&rot;d=0;
    	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1;
    	*rt=x;
    }
    #define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0))
    #define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0))
    int query(int rt,int x,int y)
    {
    	if(!rt||get_dis_min(rt,x,y)>k)return 0;
    	if(get_dis_max(rt,x,y)<=k)return tr[rt].sum;int ret=0;
    	if(abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y)<=k)ret+=tr[rt].w;
    	return ret+query(ls,x,y)+query(rs,x,y);
    }char s[10];
    int main()
    {
    	scanf("%d%d",&n,&Q);
    	for(int i=1,x;i<=n;i++)scanf("%d",&x),tr[i].p[0]=i,tr[i].p[1]=a[i]=x,tr[i].w=1;rot=build(1,n,0);
    	while(Q--)
    	{
    		int x,y;scanf("%s%d%d",s,&x,&y);
    		if(s[0]=='Q')k=y,printf("%d
    ",query(rot,x,a[x]));
    		else
    		{
    			tr[++n].p[0]=tr[n].mx[0]=tr[n].mn[0]=x;
    			tr[n].p[1]=tr[n].mx[1]=tr[n].mn[1]=y;
    			tr[n].w=tr[n].sum=1;a[x]=y;insert(n);
    		}
    	}
    	return 0;
    }

    BZOJ4520: [Cqoi2016]K远点对 & BZOJ2626: JZPFAR

    分析:考虑暴力怎么求,维护一个有K个元素的小根堆,之后枚举n^2压入和弹出即可。那么KD-Tree干什么呢?剪枝!类似曼哈顿距离最大...只是变成了和堆顶比较...

    附上代码:

    4520:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    #define N 100005
    #define inf (1ll<<60)
    #define ll long long
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define squ(x) ((ll)(x)*(x))
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1])
    #define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0])
    #define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y))
    #define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y)))
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
    char buf[1000000],*p1,*p2;
    __attribute__((optimize("-O3")))inline int rd()
    {
    	register int x=0;register char ch=nc();
    	while(ch<'0'||ch>'9') ch=nc();
    	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x;
    }
    int d,rot,n,k;__attribute__((optimize("-O3")))priority_queue<ll>q;
    struct KDtree
    {
    	int ch[2],p[2],mx[2],mn[2];
    	__attribute__((optimize("-O3")))friend bool operator<(const KDtree &a,const KDtree &b)
    	{
    		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
    	}
    }tr[N];
    __attribute__((optimize("-O3")))int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
    	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
    }
    __attribute__((optimize("-O3")))void query(int rt,int x,int y)
    {
    	ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf;
    	if(-q.top()<dis_rt)q.pop(),q.push(-dis_rt);
    	if(dis_ls>dis_rs){if(dis_ls>-q.top())query(ls,x,y);if(dis_rs>-q.top())query(rs,x,y);}
    	else{if(dis_rs>-q.top())query(rs,x,y);if(dis_ls>-q.top())query(ls,x,y);}
    }
    __attribute__((optimize("-O3")))int main()
    {
    	n=rd();k=rd();k=k<<1;
    	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
    	for(int i=1;i<=k;i++)q.push(0);for(int i=1;i<=n;i++)query(rot,tr[i].p[0],tr[i].p[1]);
    	printf("%lld
    ",-q.top());
    }
    

    2626:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    #define N 100005
    #define inf (1ll<<60)
    #define ll long long
    #define ls tr[rt].ch[0]
    #define rs tr[rt].ch[1]
    #define squ(x) ((ll)(x)*(x))
    #define max(a,b) ((a)<(b)?(b):(a))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1])
    #define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0])
    #define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y))
    #define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y)))
    #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
    char buf[1000000],*p1,*p2;
    __attribute__((optimize("-O3")))inline int rd()
    {
    	register int x=0,f=1;register char ch=nc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
    	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    	return x*f;
    }
    int d,rot,n,Q;priority_queue<pair<ll,int> >q;
    struct KDtree{int p[2],mn[2],mx[2],idx,ch[2];}tr[N];
    __attribute__((optimize("-O3")))bool cmp(const KDtree &a,const KDtree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];}
    __attribute__((optimize("-O3")))int build(int l,int r,int flag)
    {
    	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1,cmp);clear(rt);
    	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
    }
    __attribute__((optimize("-O3")))void query(int rt,int x,int y)
    {
    	ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf;
    	q.push(make_pair(-dis_rt,tr[rt].idx));q.pop();
    	if(dis_ls>dis_rs){if(dis_ls>=-q.top().first)query(ls,x,y);if(dis_rs>=-q.top().first)query(rs,x,y);}
    	else {if(dis_rs>=-q.top().first)query(rs,x,y);if(dis_ls>=-q.top().first)query(ls,x,y);}
    }
    __attribute__((optimize("-O3")))int main()
    {
    	n=rd();for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd(),tr[i].idx=i;rot=build(1,n,0);
    	Q=rd();while(Q--)
    	{
    		int x=rd(),y=rd(),z=rd();while(!q.empty())q.pop();
    		for(int i=1;i<=z;i++)q.push(make_pair(0,1<<30));
    		query(rot,x,y);printf("%d
    ",q.top().second);
    	}return 0;
    }

    那么先讲到这里吧...毕竟什么崂山白水我也不会...替罪羊树我也不会...我也就没有写...得了得了...不多说了,去做KD-Tree的课件了...过不了多久就会上传的!

    转载于:https://www.cnblogs.com/Winniechen/p/9274473.html

  • 相关阅读:
    各种模板
    HNOI2019总结
    WC2019游记
    THUSC2017 Day1题解
    NOIP2018联赛总结
    LOJ2557. 「CTSC2018」组合数问题
    NOI2018游记
    bzoj4671: 异或图
    sg函数小结
    [NOI2011]Noi嘉年华
  • 原文地址:https://www.cnblogs.com/twodog/p/12136542.html
Copyright © 2020-2023  润新知