bzoj 2648 SJY摆棋子

bzoj 2648 SJY摆棋子

  • 钱限题.题面可以看这里.
  • 若对每个点都算一遍距离,显然 \(T\) 爆,在此思想基础上使用 \(kd-tree\) 配合估价函数来剪枝效果较好.
  • 具体来说,对于 \(kd-tree\) 上的一个节点,我们知道它这颗子树所管辖的范围,算出我们查询的节点到这个范围内可能的最小距离(下界),若这个距离都比当前记录的答案劣或相等,再搜索这颗子树显然没有意义.
  • 这样就完成了最优性剪枝.
  • 还有一个剪枝,若左子树的距离下界小于右子树的距离下界,则应先搜索左子树,否则先搜索右子树.
  • 感性理解一下,若一个儿子的距离下界小,先搜索它,答案更有可能变得更小,如果小于或等于了另一个儿子的距离下界,就没有必要再搜另一个了
  • 对于加点的操作,就和二叉搜索树一样,从根节点往下,通过比较确定往哪边走,走到适当的位置插入即可.迭代代替递归,常数较小,但要注意走的同时用新节点更新当前节点的信息.
  • 插入过多时,由于 \(kd-tree\) 无法进行旋转操作保持平衡,树高可能会变得很大,使其退化成链,有三种方法处理.
    • 1.记录一个平衡因子 \(\alpha\) ,像替罪羊树那样,及时拍扁重构.这样写最稳定,时间上(应该是)最优的.
    • 2.不及时重构,每加入一定量的节点后对整棵树重构,这个量大概在 \(10^4\) 级别,可自行调整.这样写不太稳定,但实现非常简单,只多了几行.
    • 3.离线处理,将所有加点操作都读进来,一开始就全部建好,给每个点加个标记表示是否已经被插入(可用),真正插入时修改标记.这种写法不太推荐,每种操作都要额外考虑标记问题,比较繁琐,而且在资瓷离线的情况下,使用一些离线算法,编写难度和时间效率都远胜于 \(kd-tree\) .

\(kd-tree\) 最近点问题下大概是 \(O(玄学)\) , 处理矩形问题下最坏是 \(O(kn^{1-\frac 1 k})\) ???不会证明,有 \(dalao\) 得到了证明或证伪麻烦告知...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
	int x=0;
	bool pos=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			pos=0;
	for(;isdigit(ch);ch=getchar())
		x=x*10+ch-'0';
	return pos?x:-x;
}
const int MAXN=5e5+10;
int n,m,Dimen;
int rt,K=25000;
struct node{
	int v[2];
	int mi[2],ma[2];
	int ls,rs;
	bool operator < (const node &rhs) const
		{
			return v[Dimen]<rhs.v[Dimen]; 
		}
}Tree[MAXN<<1],A[MAXN<<1];
#define root Tree[o]
#define lson Tree[root.ls]
#define rson Tree[root.rs]
#define inf 1e9
void init()
{
	for(int i=0;i<2;++i)
		{
			Tree[0].mi[i]=inf;
			Tree[0].ma[i]=-inf;
		}
}
inline void pushup(int o)
{
	for(int i=0;i<2;++i)
		{
			root.mi[i]=min(root.mi[i],min(lson.mi[i],rson.mi[i]));
			root.ma[i]=max(root.ma[i],max(lson.ma[i],rson.ma[i]));
		}
}
int BuildTree(int l,int r,int dimen)
{
	Dimen=dimen;
	int mid=(l+r)>>1;
	int o=mid;
	nth_element(A+l,A+mid,A+r+1);
	for(int i=0;i<2;++i)
		{
			root.v[i]=A[mid].v[i];
			root.mi[i]=root.ma[i]=root.v[i];
		}
	root.ls=root.rs=0;
	if(l<=mid-1)
		root.ls=BuildTree(l,mid-1,dimen^1);
	if(mid+1<=r)
		root.rs=BuildTree(mid+1,r,dimen^1);
	pushup(o);
	return o;
}
node querynode;
int ans;//最近距离 
int estimate(int o)//估计o到querynode的距离 
{
	if(!o)
		return inf;
	int res=0;
	for(int i=0;i<2;++i)
		{
			if(querynode.v[i]<root.mi[i])
				res+=root.mi[i]-querynode.v[i];
			else if(querynode.v[i]>root.ma[i])
				res+=querynode.v[i]-root.ma[i];
		}
	return res;
}
void query(int o)
{
	int dist=abs(root.v[0]-querynode.v[0])+abs(root.v[1]-querynode.v[1]);//实际距离 
	ans=min(ans,dist);
	int dl=estimate(root.ls),dr=estimate(root.rs);
	if(dl<dr)
		{
			if(dl<ans)
				query(root.ls);
			if(dr<ans)
				query(root.rs);
		}
	else
		{
			if(dr<ans)
				query(root.rs);
			if(dl<ans)
				query(root.ls);
		}
}
void Insert()//像平衡树一样,比较大小向下走,走到合适的地方接上 
{
	++n;
	for(int i=0;i<2;++i)
		{
			A[n].v[i]=Tree[n].v[i]=read();
			Tree[n].mi[i]=Tree[n].ma[i]=Tree[n].v[i];
		}
	for(int o=rt,dimen=0;o;dimen^=1)
		{
			for(int i=0;i<2;++i)
				{
					root.mi[i]=min(root.mi[i],Tree[n].mi[i]);
					root.ma[i]=max(root.ma[i],Tree[n].ma[i]);
				}
			if(Tree[n].v[dimen]<root.v[dimen])
				{
					if(root.ls)
						o=root.ls;
					else
						{
							root.ls=n;
							return;
						}
				}
			else
				{
					if(root.rs)
						o=root.rs;
					else
						{
							root.rs=n;
							return;
						}
				}
		}
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i)
		for(int j=0;j<2;++j)
			A[i].v[j]=read();
	init();
	rt=BuildTree(1,n,0);
	while(m--)
		{
			int op=read();
			if(op==1)
				{
					Insert();
					if(n%K==0)
						rt=BuildTree(1,n,0);
				}
			else
				{
					querynode.v[0]=read();
					querynode.v[1]=read();
					ans=inf;
					query(rt);
					printf("%d\n",ans);
				}
		}
	return 0;
}
posted @ 2019-02-20 15:20  jklover  阅读(133)  评论(0编辑  收藏  举报