来省队集训被吊打,于是无聊学了一下kd-tree,挺好玩的东西。
kd-tree是一种支持查询平面最近点(其实是k维,但这种题目比较少)的数据结构,单次理论时间复杂度O(√n),但实际是非常快的,可以说kd-tree就是一种比较优化的暴力。
kd-tree的思想是将平面进行分割,第一次按第一维分割,第二次按第二维分割,然后接着按第一维分割,直至每个叶子节点只包含一个点,类似线段树的结构。查询时,先查询到叶子节点,然后通过回溯,对于每一个节点算出,最小的可能距离,如果小于当前的ans,就向下查询。同样,kd-tree也可以建成平衡树的模式,这样的优势是可以插入节点。
模板:
bzoj1941 http://www.lydsy.com/JudgeOnline/problem.php?id=1941
#include
#include
#include
#include
#include
#include
#define maxn 500010
#define inf 1000000000
using namespace std;
int n,m,cur,ans,root;
int x[maxn],y[maxn];
struct P
{
int mn[2],mx[2],d[2],lch,rch;
int& operator[](int x) {return d[x];}
friend bool operator<(P x,P y) {return x[cur]mid) t[mid].rch=build(mid+1,r,now^1);
update(mid);
return mid;
}
int getmn(P x)
{
int ans=0;
for (int i=0;i<2;i++)
{
ans+=max(T[i]-x.mx[i],0);
ans+=max(x.mn[i]-T[i],0);
}
return ans;
}
int getmx(P x)
{
int ans=0;
for (int i=0;i<2;i++) ans+=max(abs(T[i]-x.mn[i]),abs(T[i]-x.mx[i]));
return ans;
}
void querymx(int k)
{
ans=max(ans,dis(t[k],T));
int l=t[k].lch,r=t[k].rch,dl=-inf,dr=-inf;
if (l) dl=getmx(t[l]);
if (r) dr=getmx(t[r]);
if (dl>dr)
{
if (dl>ans) querymx(l);
if (dr>ans) querymx(r);
}
else
{
if (dr>ans) querymx(r);
if (dl>ans) querymx(l);
}
}
void querymn(int k)
{
if (dis(t[k],T)) ans=min(ans,dis(t[k],T));
int l=t[k].lch,r=t[k].rch,dl=inf,dr=inf;
if (l) dl=getmn(t[l]);
if (r) dr=getmn(t[r]);
if (dl
bzoj2716/2648 http://www.lydsy.com/JudgeOnline/problem.php?id=2716
这才是真正的kdtree模板,带上一个插入操作,类似平衡树的思想。
#include
#include
#include
#include
#include
#include
#define maxn 1000010
#define inf 1000000000
using namespace std;
int n,m,root,ans,cur;
int fabs(int x)
{
if (x>=0) return x;
else return -x;
}
struct P
{
int d[2],mx[2],mn[2],lch,rch;
int& operator[](int x) {return d[x];}
friend bool operator<(P x,P y) {return x[cur]mid) t[mid].rch=build(mid+1,r,now^1);
update(mid);
return mid;
}
int getmn(P x)
{
int tmp=0;
for (int i=0;i<2;i++)
{
tmp+=max(T[i]-x.mx[i],0);
tmp+=max(x.mn[i]-T[i],0);
}
return tmp;
}
void querymn(int k)
{
ans=min(ans,dis(t[k],T));
int l=t[k].lch,r=t[k].rch,dl=inf,dr=inf;
if (l) dl=getmn(t[l]);
if (r) dr=getmn(t[r]);
if (dl=t[k][now])
{
if (t[k].rch) insert(t[k].rch,now^1);
else
{
t[k].rch=++n;t[n]=T;
for (int i=0;i<2;i++) t[n].mx[i]=t[n].mn[i]=t[n][i];
}
}
else
{
if (t[k].lch) insert(t[k].lch,now^1);
else
{
t[k].lch=++n;t[n]=T;
for (int i=0;i<2;i++) t[n].mx[i]=t[n].mn[i]=t[n][i];
}
}
update(k);
}
int query(int x,int y)
{
ans=inf;
T[0]=x;T[1]=y;T.lch=0;T.rch=0;
querymn(root);
return ans;
}
void insert1(int x,int y)
{
T[0]=x;T[1]=y;T.lch=0;T.rch=0;insert(root,0);
}
}kdtree;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d%d",&p[i][0],&p[i][1]);
root=kdtree.build(1,n,0);
while (m--)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if (op==1) kdtree.insert1(x,y);
else printf("%d\n",kdtree.query(x,y));
}
return 0;
}
类似模板的算法,只不过换了一下估价函数,将曼哈顿距离换成了ax+by,维护一下子树的权值和来加速。
#include
#include
#include
#include
#include
#include
#define maxn 50010
#define inf 1000000000
using namespace std;
int n,m,root,cur;
long long a,b,c;
long long ans;
struct P
{
int d[2],mx[2],mn[2],lch,rch,key;
long long sum;
int& operator[](int x) {return d[x];}
friend bool operator<(P a,P b) {return a[cur]mid) t[mid].rch=build(mid+1,r,now^1);
update(mid);
return mid;
}
void query(int k)
{
if (check(t[k][0],t[k][1])) ans+=t[k].key;
int l=t[k].lch,r=t[k].rch,dl=0,dr=0;
if (l) dl=cal(t[l]);
if (r) dr=cal(t[r]);
if (dl==4) ans+=t[l].sum;
else if (dl) query(l);
if (dr==4) ans+=t[r].sum;
else if (dr) query(r);
}
}kdtree;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d%d%d",&p[i][0],&p[i][1],&p[i].key);
root=kdtree.build(1,n,0);
while (m--)
{
scanf("%lld%lld%lld",&a,&b,&c);
ans=0;
kdtree.query(root);
printf("%lld\n",ans);
}
return 0;
}