【清华集训2014】卡常数

前言

好像K-D树也不是很难啊,为啥以前就学不会呢?

就把这道题当做K-D树入门题好了,只需要记住K-D树就是每次轮换一维分割空间即可。

题目

UOJ

DarkBZOJ

题目大意:

随机给 \(n\) 个三维点,\(m\) 次操作,每次操作可以修改一个点的坐标,或者在线询问哪个点在一个球的球面上(保证有且仅有一个点),球的坐标和半径是每次给的。

\(1\le n,m\le 65536.\) 坐标的绝对值在 \(100\) 以内。

讲解

值得注意的是题目建议开long double以保证精度,但是实际上double就可以过,而我的代码开long double在DarkBZOJ上会T一个点...

首先我们要知道这个在线数据怎么解密,注意它给的是加密方式以及加密后的值!需要反向解密

设函数 \(f(x)=ax-b\sin x\),加密后的值为 \(f(lastans\times 原值+1)\),乍一看完全不会,但是我们注意到 \(0\le a<b<5\) 的关键条件,这决定了这个函数的单调性,于是可以二分解密。实数二分的小技巧是用次数作阈值而非精度。

接下来就是K-D树的模板部分了,我们直接建树,然后暴力询问,用边界判断后剪枝,具体来说剪枝就是维护K-D树的时候记录一下子树所围成的长方体,然后判断球与其是否有交,如果有再递归下去找。

而且由于这道题数据随机,所以不需要用替罪羊的思路重新建树,修改直接打删除标记然后暴力插入即可。

不是很会算时间复杂度qwq

代码

//12252024832524
//cz_xuyixuan orz
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 65536 << 2;
const int INF = 0x3f3f3f3f;
const double L = -100;
const double R = 100;
const double eps = 1e-6;
int n,m,nxt[3] = {1,2,0};
double A,B,lst = 0.1;

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
double Readf(){double ret;scanf("%lf",&ret);return ret;}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return y < x ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int tot,rt,mode,pos[MAXN];
struct point
{
	double x,y,z;
	void Get(){x = Readf();y = Readf();z = Readf();}
};
bool operator < (point P,point Q)
{
	if(!mode) return P.x < Q.x;
	else if(mode == 1) return P.y < Q.y;
	else return P.z < Q.z;
}
struct Info{int ID;point p;}tmp[MAXN];
bool operator < (Info P,Info Q){return P.p < Q.p;}

struct node
{
	point l,r;
	Info info;
	int ch[2],fa;
	bool used;
}t[MAXN];
double sq(double x){return x*x;}
double dis(point P,point Q){return sqrt(sq(P.x-Q.x)+sq(P.y-Q.y)+sq(P.z-Q.z));}
#define lc t[x].ch[0]
#define rc t[x].ch[1]
#define lx t[x].l.x
#define ly t[x].l.y
#define lz t[x].l.z
#define rx t[x].r.x
#define ry t[x].r.y
#define rz t[x].r.z
void up1(int x,int son)//pot1 & 4
{
	if(!son) return;
	lx = Min(lx,t[son].l.x);
	ly = Min(ly,t[son].l.y);
	lz = Min(lz,t[son].l.z);
	rx = Max(rx,t[son].r.x);
	ry = Max(ry,t[son].r.y);
	rz = Max(rz,t[son].r.z);
}
void up(int x)
{
	if(t[x].used) t[x].l = t[x].r = t[x].info.p;
	else t[x].l = point{INF,INF,INF},t[x].r = point{-INF,-INF,-INF};
	up1(x,lc); up1(x,rc);
}
void Build(int &x,int l,int r,int now)
{
	x = ++tot; mode = now;
	int mid = (l+r) >> 1;
	nth_element(tmp+l,tmp+mid,tmp+r+1);
	t[x].info = tmp[mid]; t[x].used = 1; pos[t[x].info.ID] = x;//pot2
	if(l < mid) Build(lc,l,mid-1,nxt[now]),t[lc].fa = x;
	if(mid < r) Build(rc,mid+1,r,nxt[now]),t[rc].fa = x;
	up(x); 
}
void access(int x)
{
	if(!x) return;
	up(x); access(t[x].fa);
}
void ins(int &x,Info val,int now)
{
	if(!x)
	{
		pos[val.ID] = x = ++tot;
		t[x].info = val;
		t[x].used = 1;
		up(x);
		return;
	}
	mode = now;
	if(val < t[x].info) ins(lc,val,nxt[now]),t[lc].fa = x;
	else ins(rc,val,nxt[now]),t[rc].fa = x;
	up(x);//DO NOT FORGET TO DO IT!
}
void Modify(int ID,point p)
{
	t[pos[ID]].used = 0; access(pos[ID]);
	ins(rt,Info{ID,p},0);
}
double closest(int x,point p)
{
	double X,Y,Z;
	if(p.x < lx) X = lx - p.x;
	else if(p.x > rx) X = p.x - rx;
	else X = 0;
	if(p.y < ly) Y = ly - p.y;
	else if(p.y > ry) Y = p.y - ry;
	else Y = 0;
	if(p.z < lz) Z = lz - p.z;
	else if(p.z > rz) Z = p.z - rz;
	else Z = 0;
	return sqrt(sq(X)+sq(Y)+sq(Z));
}
double farthest(int x,point p){return sqrt(sq(Max(Abs(lx-p.x),Abs(rx-p.x)))+sq(Max(Abs(ly-p.y),Abs(ry-p.y)))+sq(Max(Abs(lz-p.z),Abs(rz-p.z))));}
int Query(int x,point p,double r)
{
	if(!x || closest(x,p) > r+eps || farthest(x,p) < r-eps) return 0;//pot3
	if(t[x].used && Abs(dis(t[x].info.p,p)-r) < eps) return t[x].info.ID;
	int ret = Query(lc,p,r);
	if(ret) return ret;
	return Query(rc,p,r);
}
void print(int x)
{
	if(lc) print(lc);
	if(rc) print(rc);
	up(x);
}
double f(double x){return A*x-B*sin(x);}
double decrypt(double l,double r,double x)
{
	for(int i = 1;i <= 40;++ i)
	{
		double mid = (l+r) / 2;
		if(f(lst*mid+1) < x) l = mid;
		else r = mid;
	}
	return (l+r) / 2;
}

int main()
{
//	freopen("C:\\Users\\Administrator\\Desktop\\1.in","r",stdin);
//	freopen("C:\\Users\\Administrator\\Desktop\\mine.out","w",stdout);
	n = Read(); m = Read(); A = Readf(); B = Readf();
	for(int i = 1;i <= n;++ i) tmp[i].p.Get(),tmp[i].ID = i;
	Build(rt,1,n,0);
	while(m --> 0)
	{
		if(!Read())//modify
		{
			int ID = round(decrypt(1,n,Readf()));
			double x = decrypt(L,R,Readf()),y = decrypt(L,R,Readf()),z = decrypt(L,R,Readf());
			Modify(ID,point{x,y,z}); 
		}
		else//query
		{
			double x = decrypt(L,R,Readf()),y = decrypt(L,R,Readf()),z = decrypt(L,R,Readf()),r = decrypt(0,350,Readf());//pot5
			lst = Query(rt,point{x,y,z},r);
			Put((int)lst,'\n');
		}
	}
	return 0;
}
posted @ 2022-02-07 09:35  皮皮刘  阅读(103)  评论(0编辑  收藏  举报