首先是普通平衡树(替罪羊树):
#include<bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
const int N=2e6+10;
struct ScapegoatTree
{
int l,r,val;
int sz,sd;//树中总点数,树中不包含已被删除的点的点数,用于判断是否重构
int cnt;//该数据出现次数,为0表示已删除
int size;//平衡树中存在的数据个数(即每个点记cnt次)
}tr[N];
double alpha=0.75;
int root,tot,tzy[N];
int New(int val)
{
tr[++tot]={0,0,val,1,1,1,1};
return tot;
}
void pushup(int p)
{
tr[p].sz=tr[tr[p].l].sz+tr[tr[p].r].sz+1;
tr[p].sd=tr[tr[p].l].sd+tr[tr[p].r].sd+(tr[p].cnt!=0);
tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
}
int build(int l,int r)//将tzy数组中l~r的节点重新建成一棵树
{
if(l>r)return 0;
int mid=l+r>>1;
tr[tzy[mid]].l=build(l,mid-1);
tr[tzy[mid]].r=build(mid+1,r);
pushup(tzy[mid]);
return tzy[mid];
}
void dfs(int p,int &num)//将以p为根的子树的节点取出
{
if(!p)return;
dfs(tr[p].l,num);
if(tr[p].cnt)tzy[++num]=p;
dfs(tr[p].r,num);
}
void rebuild(int &p)
{
if(!tr[p].cnt)return;//当前节点不存在,无法重建
if(max(tr[tr[p].l].sz,tr[tr[p].r].sz)<=alpha*tr[p].sz)return;//不平衡程度在可接受范围内
if(tr[p].sd<=tr[p].sz*alpha)return;//不存在的点的数量在可接受范围内
int num=0;dfs(p,num);
p=build(1,num);
}
void insert(int &p,int val)
{
if(!p){p=New(val);return;}
if(tr[p].val==val)++tr[p].cnt;
else if(tr[p].val>val)insert(tr[p].l,val);
else insert(tr[p].r,val);
pushup(p);rebuild(p);
}
void erase(int &p,int val)//惰性删除
{
if(!p)return;
if(tr[p].val==val&&tr[p].cnt)--tr[p].cnt;
else if(tr[p].val>val)erase(tr[p].l,val);
else erase(tr[p].r,val);
pushup(p);rebuild(p);
}
//以上操作很重要······
int get_rank(int p,int val)
{
if(!p)return 1;
if(tr[p].val==val)return tr[tr[p].l].size+1;
if(tr[p].val>val)return get_rank(tr[p].l,val);
return tr[tr[p].l].size+tr[p].cnt+get_rank(tr[p].r,val);
}
int get_val(int p,int rank)//不存在返回int最大值
{
if(!p)return INF;
if(tr[tr[p].l].size>=rank)return get_val(tr[p].l,rank);
if(tr[tr[p].l].size+tr[p].cnt>=rank)return tr[p].val;
return get_val(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>tzy[i];
sort(tzy+1,tzy+n+1);
int num=0;
for(int i=1;i<=n;++i)
{
if(num>0&&tzy[i]==tr[tzy[num]].val)
++tr[tzy[num]].cnt;
else tzy[++num]=New(tzy[i]);
}
root=build(1,num);//对初始值手动建树
int last=0,ans=0;
while(m--)
{
int op,x;
cin>>op>>x;
x^=last;
if(op==1)insert(root,x);
else if(op==2)erase(root,x);
else if(op==3)last=get_rank(root,x);
else if(op==4)last=get_val(root,x);
else if(op==5)last=get_val(root,get_rank(root,x)-1);
else last=get_val(root,get_rank(root,x+1));
if(op>=3)ans^=last;
}
cout<<ans<<'\n';
return 0;
}
现在,我们一起来感受一下K-D Tree的魅力吧!!!
简单题
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
namespace _KDTree{
const int N=5e5+10;
const int K=3;//树中为K-1维空间的点,如果维度增加,修改K即可
const double alpha=0.725;
int root,tot,kd;
int st[N],top;
struct Point{int num[K],val;};
struct Node
{
int l,r,d;//d表示子树以维度d进行划分
int size,sum;//sum记录子树点上的权值和
int minn[K],maxn[K];//记录子树中点的坐标在每个维度上的最小值和最大值
Point p;
}tr[N];
#define ls tr[now].l
#define rs tr[now].r
void pushup(int now)//更新size,sum,minn,maxn
{
for(int i=1;i<K;++i)
{
tr[now].minn[i]=tr[now].maxn[i]=tr[now].p.num[i];
if(ls){
tr[now].minn[i]=min(tr[now].minn[i],tr[ls].minn[i]);
tr[now].maxn[i]=max(tr[now].maxn[i],tr[ls].maxn[i]);
}
if(rs){
tr[now].minn[i]=min(tr[now].minn[i],tr[rs].minn[i]);
tr[now].maxn[i]=max(tr[now].maxn[i],tr[rs].maxn[i]);
}
}
tr[now].sum=tr[ls].sum+tr[rs].sum+tr[now].p.val;
tr[now].size=tr[ls].size+tr[rs].size+1;
}
bool cmp(int a,int b)//辅助build
{
return tr[a].p.num[kd]<tr[b].p.num[kd];
}
int build(int l,int r)//建树
{
if(l>r)return 0;
int mid=l+r>>1;
double Max=-1;
for(int i=1;i<K;++i)
{
double avg=0;//平均数
for(int j=l;j<=r;++j)avg+=tr[st[j]].p.num[i];
avg/=(r-l+1);
double var=0;//求当前维度上的方差
for(int j=l;j<=r;++j)var+=(tr[st[j]].p.num[i]-avg)*(tr[st[j]].p.num[i]-avg);
if(var>Max)
{
Max=var;
kd=i;
}
}
nth_element(st+l,st+mid,st+r+1,cmp);
tr[st[mid]].d=kd;
tr[st[mid]].l=build(l,mid-1);
tr[st[mid]].r=build(mid+1,r);
pushup(st[mid]);
return st[mid];
}
void dfs(int now)//收集子树节点编号
{
if(!now)return;
dfs(ls);
st[++top]=now;
dfs(rs);
}
void rebuild(int &now)//重构
{
if(max(tr[ls].size,tr[rs].size)<alpha*tr[now].size)return;
top=0;dfs(now);
now=build(1,top);
}
//以上为核心操作,是保证时间复杂度的关键······
void insert(int &now,Point p)
{
if(!now)
{
now=++tot;
tr[now].p=p;
tr[now].d=(unsigned)p.val%(K-1)+1;//随机一个划分维度
pushup(now);
return;
}
int d=tr[now].d;
if(tr[now].p.num[d]>=p.num[d])insert(ls,p);
else insert(rs,p);
pushup(now);rebuild(now);
}
//给出查询矩形的左下角和右上角
bool inside(int x1,int y1,int x2,int y2,int a,int b,int c,int d){
return x1<=a&&y1<=b&&x2>=c&&y2>=d;//子树矩形被查询矩形完全包含
}
bool outside(int x1,int y1,int x2,int y2,int a,int b,int c,int d){
return x1>c||x2<a||y1>d||y2<b;//子树矩形与查询矩形无交集
}
int query(int now,int x1,int y1,int x2,int y2)
{
if(outside(x1,y1,x2,y2,tr[now].minn[1],tr[now].minn[2],tr[now].maxn[1],tr[now].maxn[2]))return 0;
if(inside(x1,y1,x2,y2,tr[now].minn[1],tr[now].minn[2],tr[now].maxn[1],tr[now].maxn[2]))return tr[now].sum;
int ans=0;
if(inside(x1,y1,x2,y2,tr[now].p.num[1],tr[now].p.num[2],tr[now].p.num[1],tr[now].p.num[2]))ans+=tr[now].p.val;
ans+=query(ls,x1,y1,x2,y2)+query(rs,x1,y1,x2,y2);
return ans;
}
#undef ls
#undef rs
}using namespace _KDTree;
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int n;cin>>n;//一句废话,没啥用
Point p;
int x1,y1,x2,y2;
int op,lastans=0;
while(cin>>op&&op!=3)
{
if(op==1)
{
cin>>p.num[1]>>p.num[2]>>p.val;
p.num[1]^=lastans;
p.num[2]^=lastans;
p.val^=lastans;
insert(root,p);
}
else if(op==2)
{
cin>>x1>>y1>>x2>>y2;
x1^=lastans;
y1^=lastans;
x2^=lastans;
y2^=lastans;
cout<<(lastans=query(root,x1,y1,x2,y2))<<'\n';
}
}
return 0;
}
K-D Tree本质上是一种暴力的数据结构,但可以通过剪枝或者启发式搜索等优化技巧来降低期望复杂度,虽然很多时候最坏情况下的时间复杂度还是可以让你T飞。
k远点对
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue<ll,vector<ll>,greater<ll>>q;
namespace _KDTree{//本题没有插入和删除操作,无需重构。
const int N=5e5+10;
const int K=3;//树中为K-1维空间的点,如果维度增加,修改K即可
int root,tot,kd;
int st[N],top;
struct Point{int num[K];}p[N];//这次不将坐标点直接放在树上的点中,不然坐标点还要再存一次
struct Node
{
int l,r;
int minn[K],maxn[K];//记录子树中点的坐标在每个维度上的最小值和最大值
int id;//记录坐标点的编号
}tr[N];
#define ls tr[now].l
#define rs tr[now].r
void pushup(int now)//更新minn,maxn
{
for(int i=1;i<K;++i)
{
tr[now].minn[i]=tr[now].maxn[i]=p[tr[now].id].num[i];
if(ls){
tr[now].minn[i]=min(tr[now].minn[i],tr[ls].minn[i]);
tr[now].maxn[i]=max(tr[now].maxn[i],tr[ls].maxn[i]);
}
if(rs){
tr[now].minn[i]=min(tr[now].minn[i],tr[rs].minn[i]);
tr[now].maxn[i]=max(tr[now].maxn[i],tr[rs].maxn[i]);
}
}
}
bool cmp(int a,int b)//辅助build
{
return p[tr[a].id].num[kd]<p[tr[b].id].num[kd];
}
int build(int l,int r)//建树
{
if(l>r)return 0;
int mid=l+r>>1;
double Max=-1;
for(int i=1;i<K;++i)
{
double avg=0;//平均数
for(int j=l;j<=r;++j)avg+=p[tr[st[j]].id].num[i];
avg/=(r-l+1);
double var=0;//求当前维度上的方差
for(int j=l;j<=r;++j)var+=(p[tr[st[j]].id].num[i]-avg)*(p[tr[st[j]].id].num[i]-avg);
if(var>Max)
{
Max=var;
kd=i;
}
}
nth_element(st+l,st+mid,st+r+1,cmp);
tr[st[mid]].l=build(l,mid-1);
tr[st[mid]].r=build(mid+1,r);
pushup(st[mid]);
return st[mid];
}
ll sqr(ll x){return x*x;}
ll dist(int now,int x)//计算点与子树矩形的最远距离
{
return max(sqr(p[x].num[1]-tr[now].minn[1]),sqr(p[x].num[1]-tr[now].maxn[1]))+
max(sqr(p[x].num[2]-tr[now].minn[2]),sqr(p[x].num[2]-tr[now].maxn[2]));
}
void query(int now,int x)
{
if(!now)return;
ll t=sqr(p[tr[now].id].num[1]-p[x].num[1])+sqr(p[tr[now].id].num[2]-p[x].num[2]);
if(t>q.top())q.pop(),q.push(t);
ll disl=dist(ls,x),disr=dist(rs,x);
if(disl>q.top()&&disr>q.top())//启发式搜索,优先搜索距离值更大的子树,这样另一棵子树就可能不会再搜索
{
if(disl>disr)
{
query(ls,x);
if(disr>q.top())query(rs,x);
}
else
{
query(rs,x);
if(disl>q.top())query(ls,x);
}
}
else
{
if(disl>q.top())query(ls,x);
if(disr>q.top())query(rs,x);
}
}
#undef ls
#undef rs
}using namespace _KDTree;
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int n,k;
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>p[i].num[1]>>p[i].num[2];
for(int i=1;i<=n;++i)
{
++tot;
tr[tot].id=i;
st[++top]=tot;
}
k*=2;
root=build(1,top);
for(int i=1;i<=k;++i)q.push(0);
for(int i=1;i<=n;++i)query(root,i);
cout<<q.top()<<'\n';
return 0;
}
完结撒花!!!