2716: [Violet 3]天使玩偶
Description
Input
Output
终于,第一棵KDtree ,撒花~~~撒花~~~撒花~~~
才知道这道题用4个CDQ容斥不是一般的快,但是80SEC的时间限制给kdtree亮了绿灯!
主要思想先把所有操作离线,对包括所有插入节点的点集建树,把出现的点做一个exist
标记,不出现的点先不初始化,也就是不用它的信息来pushup但是需要传递子树信息,
然后对于每一个插入操作的节点记下树上位置,处理到插入操作直接初始化节点,exist=1就好了。
主要就是build函数非常坑,另外就是查询操作要留心
#include<cstdio>
#include<algorithm>
#include<cmath>
/*BZOJ2716 离线操作 对拍成功*/
using namespace std;
const int maxn=5e5+100;
const int inf=0x3f3f3f3f;
int opt[maxn*2][3];
int id[maxn*2];
int n,m;
namespace KD_tree{
struct node{
int ch[2],mn[2],mx[2],x,y,fa,id;
bool d,exist;
node(){mn[0]=mn[1]=inf;exist=0;}
node(int rx,int ry){x=rx,y=ry;exist=0;}
void init(){
mx[0]=mn[0]=x;
mx[1]=mn[1]=y;
}
}tree[maxn*4];
int cnt,D,root;
bool operator <(const node &a,const node &b){
return !D?a.y<b.y:a.x<b.x;
}
void push_up(int rt){
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
tree[rt].mn[j]=min(tree[rt].mn[j],tree[tree[rt].ch[i]].mn[j]);
tree[rt].mx[j]=max(tree[rt].mx[j],tree[tree[rt].ch[i]].mx[j]);
}
}
void build(int &rt,int l,int r,int d,int fa){
if(l==r){rt=r;tree[rt].d=d;tree[rt].fa=fa;tree[rt].d=d;return;}
int mid=(l+r)/2;
rt=mid;D=d; /*这里的细节问题一定要注意啊,要不然直接WA+TLE双喜临门*/
nth_element(tree+l,tree+mid,tree+r+1);
tree[rt].d=d;tree[rt].fa=fa;
if(l<mid) build(tree[rt].ch[0],l,mid-1,d^1,rt);
if(r>mid) build(tree[rt].ch[1],mid+1,r,d^1,rt);
push_up(rt);
}
int dis(int x,int y,node rect){
int tmp=0;
tmp+=max(0,rect.mn[0]-x),tmp+=max(0,x-rect.mx[0]);
tmp+=max(0,rect.mn[1]-y),tmp+=max(0,y-rect.mx[1]);
return tmp;
}
int mh(int x,int y,int _x,int _y){
return abs(x-_x)+abs(y-_y);
}
void query(int x,int y,int rt,int &ans){
int d,dl=inf,dr=inf;
if(tree[rt].exist){
d=mh(x,y,tree[rt].x,tree[rt].y);
ans=min(d,ans);
}
if(tree[rt].ch[0]) dl=dis(x,y,tree[tree[rt].ch[0]]);
if(tree[rt].ch[1]) dr=dis(x,y,tree[tree[rt].ch[1]]);
if(dl<dr){
if(dl<ans) query(x,y,tree[rt].ch[0],ans);
if(dr<ans) query(x,y,tree[rt].ch[1],ans);
}else{
if(dr<ans) query(x,y,tree[rt].ch[1],ans);
if(dl<ans) query(x,y,tree[rt].ch[0],ans);
}
}
void insert(int now){
/* 注意,当前节点一旦插入,需要初始化,并且用该节点信息重新更新整条链 */
now=id[now];
tree[now].init();
tree[now].exist=1;
while(tree[now].fa){
push_up(now);
now=tree[now].fa;
}
}
}
int main(){
using namespace KD_tree;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&tree[i].x,&tree[i].y);
tree[i].exist=1;
tree[i].init();
}
cnt=n;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&opt[i][0],&opt[i][1],&opt[i][2]);
if(opt[i][0]==1) /*未插入的节点仍然需要传递下层信息,只是不传递本身信息*/
tree[++cnt].x=opt[i][1],tree[cnt].y=opt[i][2],tree[cnt].id=i;
}
build(root,1,cnt,1,0);
for(int i=1;i<=cnt;i++)
if(tree[i].id) id[tree[i].id]=i;
for(int i=1;i<=m;i++){
int x=opt[i][1],y=opt[i][2],ans=inf;
if(opt[i][0]==2){
query(x,y,root,ans);
printf("%d\n",ans);
}
if(opt[i][0]==1)
insert(i);
}
return 0;
} P.S. 另外,下面是方差划分版+读入优化
#include<cstdio>
#include<algorithm>
#include<cmath>
#define pow(x) x*x
#define mh(x,y,rect) (abs(rect.pos[0]-x)+abs(rect.pos[1]-y))
using namespace std;
const int maxn=1e6+100;
const int inf=0x3f3f3f3f;
int id[maxn];
int opt[maxn][3];
int n,m;
int read(){
char c=getchar();int ret=0;
while(c<'0' || c>'9')
c=getchar();
while(c>='0' && c<='9')
ret=ret*10+c-'0',c=getchar();
return ret;
}
namespace KD_tree{
struct node{
int ch[2],mn[2],mx[2],pos[2],d,id,fa,exist;
node(){mn[0]=mn[1]=inf;}
void init(){for(int i=0;i<2;i++) mn[i]=mx[i]=pos[i];}
}tree[maxn*2];
int D,cnt=0,root;
bool operator <(const node &a,const node &b){
return a.pos[D]<b.pos[D];
}
long long Variance(int l,int r,bool type){
long long a=0,b=0,n=(r-l+1);
for(int i=l;i<=r;i++){
a+=1LL*pow(tree[i].pos[type]);
b+=1LL*tree[i].pos[type];
}
return a/n-pow(b/n);
}
void push_up(int rt){
for(int i=0;i<2;i++)
for(int j=0;j<2;j++){
tree[rt].mn[j]=min(tree[tree[rt].ch[i]].mn[j],tree[rt].mn[j]);
tree[rt].mx[j]=max(tree[tree[rt].ch[i]].mx[j],tree[rt].mx[j]);
}
}
bool judge(int l,int r){
long long _x=0,_y=0;
_x=Variance(l,r,0);
_y=Variance(l,r,1);
return _x<_y;
}
int dis(int x,int y,node rect){
int tmp=0;
tmp+=max(x-rect.mx[0],max(rect.mn[0]-x,0));
tmp+=max(y-rect.mx[1],max(rect.mn[1]-y,0));
return tmp;
}
void build(int &rt,int l,int r,int fa){
if(l==r){rt=l;tree[l].fa=fa;return;}
int mid=(l+r)/2;
rt=mid;D=judge(l,r);
nth_element(tree+l,tree+mid,tree+r+1);
tree[rt].d=D;tree[rt].fa=fa;
if(l<mid) build(tree[rt].ch[0],l,mid-1,rt); /*注意这里不要手贱*/
if(r>mid) build(tree[rt].ch[1],mid+1,r,rt);
push_up(rt);
}
void query(int rt,int x,int y,int &ans){
int dl=inf,dr=inf,d;
if(tree[rt].exist){
d=mh(x,y,tree[rt]);
ans=min(d,ans);
}
if(tree[rt].ch[0]) dl=dis(x,y,tree[tree[rt].ch[0]]);
if(tree[rt].ch[1]) dr=dis(x,y,tree[tree[rt].ch[1]]);
if(dl<dr){
if(dl<ans) query(tree[rt].ch[0],x,y,ans);
if(dr<ans) query(tree[rt].ch[1],x,y,ans);
}else {
if(dr<ans) query(tree[rt].ch[1],x,y,ans);
if(dl<ans) query(tree[rt].ch[0],x,y,ans);
}
}
void insert(int w){
w=id[w];
tree[w].exist=1;
tree[w].init();
while(tree[w].fa){
push_up(w);
w=tree[w].fa;
}
}
}
int main(){
using namespace KD_tree;
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++){
tree[i].pos[0]=read();
tree[i].pos[1]=read();
tree[i].exist=1;
tree[i].init();
}
cnt=n;
for(int i=1;i<=m;i++){
opt[i][0]=read();
opt[i][1]=read();
opt[i][2]=read();
if(opt[i][0]==1)
tree[++cnt].id=i,tree[cnt].pos[0]=opt[i][1],tree[cnt].pos[1]=opt[i][2];
}
build(root,1,cnt,0);
for(int i=1;i<=cnt;++i)
if(!tree[i].exist)
id[tree[i].id]=i;
for(int i=1;i<=m;++i){
int x=opt[i][1],y=opt[i][2],ans=inf;
if(opt[i][0]==2){
query(root,x,y,ans);
printf("%d\n",ans);
}
if(opt[i][0]==1)
insert(i);
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
rockdu
没有帐号? 立即注册