不可视境界线

前言

总之就是很离谱。

题目

USOJ

\(1\le n\le 10^5;1\le k\le 2;\) 坐标范围在 \([0,10^4)\) 之间。

讲解

题目中的式子长得就很像距离,维护 \(dp_i\) 表示前 \(i\) 个的总伤害,把根号内看成单次伤害,发现转移的时候能把后面的 \(-a\) 抵消掉。

又有 \(k\) 维,所以我们直接上K-D树,将 \(k\) 维坐标转换为 \(k+1\) 维,前面一维加一个dp值来方便,为了避免过多开方而导致常数过大,所以我第一个值用的曼哈顿距离,不用平方。

代码

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

typedef long long LL;
const int MAXN = 100005;
const double alpha = 0.65;
const LL INF = 1ll << 60;
int n,k,rb[MAXN];
int nxt[3] = {1,2,0};

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;
}
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 x > y ? 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;
struct point{LL x[3];}tmp[MAXN];
bool operator < (point A,point B){return A.x[mode] < B.x[mode];}
#define lc t[x].ch[0]
#define rc t[x].ch[1]
struct node{point p,l,r;int ch[2],fa,siz,md;}t[MAXN];
LL sq(LL x){return x*x;}
LL dis(point A,point B)
{
	LL ret = Abs(A.x[0]-B.x[0]);
	for(int i = 1;i < k;++ i) ret += sq(A.x[i]-B.x[i]);
	return ret;
}
int newnode(){int x = rb[++tot];lc = rc = t[x].fa = 0;return x;}
void up1(int x,int son)
{
	if(!son) return;
	for(int i = 0;i < k;++ i)
		t[x].l.x[i] = Min(t[x].l.x[i],t[son].l.x[i]),t[x].r.x[i] = Max(t[x].r.x[i],t[son].r.x[i]);
	t[x].siz += t[son].siz;
}
void up(int x)
{
	t[x].l = t[x].r = t[x].p; t[x].siz = 1;
	up1(x,lc); up1(x,rc);
}
bool wc(int x){return x == t[t[x].fa].ch[1];}//which child :)
void Build(int &x,int l,int r,int now)
{
	x = newnode(); mode = now;
	int mid = (l+r) >> 1;
	nth_element(tmp+l,tmp+mid,tmp+r+1);
	t[x].p = tmp[mid]; t[x].md = now;
	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);
}
int nd,cur;
void dfs(int x)
{
	tmp[++cur] = t[rb[tot--] = x].p;
	if(lc) dfs(lc);
	if(rc) dfs(rc);
}
void pia(int x)
{
	cur = 0; dfs(x);
	if(t[x].fa) Build(t[t[x].fa].ch[wc(x)],1,cur,t[x].md);
	else Build(rt,1,cur,t[x].md);
}
point pp;
void ins(int &x,int now)
{
	if(x == rt) nd = 0;
	if(!x)
	{
		x = newnode();
		t[x].p = pp;
		t[x].md = now;
		up(x);
		return;
	}
	mode = now;
	if(pp < t[x].p) ins(lc,nxt[now]),t[lc].fa = x;
	else ins(rc,nxt[now]),t[rc].fa = x;
	up(x);
	if(Max(t[lc].siz,t[rc].siz) > t[x].siz * alpha) nd = x;
	if(x == rt && nd) pia(nd);
}
LL ans;
LL closest(int x,point p)
{
	LL ret = 0,dz;
	for(int i = 0;i < k;++ i)
	{
		dz = 0;
		if(p.x[i] < t[x].l.x[i]) dz = t[x].l.x[i]-p.x[i];
		else if(p.x[i] > t[x].r.x[i]) dz = p.x[i]-t[x].r.x[i];
		if(!i) ret += dz;
		else ret += sq(dz);
	}
	return ret;
}
void Query(int x)
{
	if(closest(x,pp) >= ans) return;
	ans = Min(ans,dis(t[x].p,pp));
	if(lc && rc)
	{
		if(closest(lc,pp) < closest(rc,pp)) Query(lc),Query(rc);
		else Query(rc),Query(lc);
	}
	else
	{
		if(lc) Query(lc);
		if(rc) Query(rc);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); k = Read()+1; 
	for(int i = 1;i <= n+1;++ i) rb[i] = i;
	if(k == 2) nxt[1] = 0;
	pp = point{0,0,0}; ins(rt,0);
	for(int i = 1;i <= n;++ i)
	{
		pp = point{0,0,0}; ans = INF;
		for(int j = 1;j < k;++ j) pp.x[j] = Read();
		Query(rt);
		pp.x[0] = ans; ins(rt,0);
		printf("%.4f\n",sqrt(ans));
	}
	return 0;
}

后记

有趣的是因为这道题没有来源,是我们内部造的数据,所以我先写了一发暴力(无替罪羊式重构),最大跑了1.9s。此时的时限是1s。

后来改成有重构版本,竟然要跑16s+,我直接震撼,后来把平衡因子从0.69改成了0.99才跑过。(???)

当然最后发现是写错了,大概就是如果发现有某个点不平衡,重构整棵树。改了就过了。

于是我加了两组卡0.99那个版本的数据,然后我的正解竟然也过不了,被迫把时限开成2s。

然后网上的标程T了,我的0.99错误版本偶尔还能过。

posted @ 2022-02-09 21:00  皮皮刘  阅读(207)  评论(2编辑  收藏  举报