前言
总之就是很离谱。
题目
\(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错误版本偶尔还能过。