解题思路
首先发现只有
x
x
x和
c
c
c是有用的
这些时空构成了一棵树,我们实际上要找一个点
i
i
i,满足对于给出的
X
X
X
m
i
n
(
(
X
−
x
i
)
2
+
c
i
)
min((X-x_i)^2+c_i)
min((X−xi)2+ci)
根据斜率优化的套路
我们展开式子
a
n
s
=
(
X
−
x
i
)
2
+
c
i
=
X
2
−
2
x
i
X
+
x
i
2
+
c
i
ans=(X-x_i)^2+c_i=X^2-2x_iX+x_i^2+c_i
ans=(X−xi)2+ci=X2−2xiX+xi2+ci
x
i
2
+
c
i
=
a
n
s
+
2
∗
x
i
∗
X
−
X
2
x_i^2+c_i=ans+2*x_i*X-X^2
xi2+ci=ans+2∗xi∗X−X2
那么可以把每个星球看成一个点
(
2
×
x
i
,
x
i
2
+
c
i
)
(2\times x_i,x_i^2+c_i)
(2×xi,xi2+ci)然后用一条斜率为
X
X
X的凸包截这些点,所能获得的最大截距
我们只需要维护一个上凸壳就行了
然后我们就得到了一个思路
我们建立一颗以树的
d
f
s
dfs
dfs为序列的线段树,这样每个星球存在的范围是若干区间,并对于每个区间都维护一个上凸壳,然后为了更方便的建立凸包,我们先把
x
x
x坐标排序,然后再把每个星球插入到它在线段树上存在的区间里
查询的时候如果在线的话每次可能吧整个凸包遍历一遍
所以我们把这些询问按照询问的斜率排序,然后对于每个节点记录一个
c
u
r
[
x
]
cur[x]
cur[x]表示当前扫描到了这个凸包的那个点,因为排过序了所以不可能访问之前的点,也就是说最多访问整个树的凸包一遍,而每个点最多在
O
(
l
o
g
n
)
O(logn)
O(logn)个区间里,所以我们的复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
对于怎么处理每个星球在线段树上的区间,可以这样考虑
首先对于
d
f
s
dfs
dfs序来说,第一个有这个星球的节点就是这个星球出现的位置
所以星球只可能在这个节点的子树内出现了
然后之后每次出现的时候,就代表删除,所以这个点的子树内的点都没有这个星球,记录一下上一次出现的位置,那么当前区间就是
[
e
d
l
a
s
t
+
1
,
d
f
n
i
]
[ed_{last}+1,dfn_i]
[edlast+1,dfni]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5+7;
typedef double db;
const LL INF = 1e18+7;
inline LL read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
int n,m;
LL C[N],X[N];
int note[N];
struct edge
{
int y,next;
}e[2*N];
struct point
{
LL x,y;
}p[N];
struct Query
{
int X,s,id;
}Q[N];
point Point(LL x,LL y)
{
return (point){x,y};
}
int link[N],t=0;
void add(int x,int y)
{
e[++t].y=y;
e[t].next=link[x];
link[x]=t;
}
int dfn[N],End[N],tot=0;
vector<int> G[N];
int id[N];
void dfs(int x)
{
dfn[x]=++tot;
G[note[x]].push_back(x);
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
dfs(y);
}
End[x]=tot;
}
bool cmp(int i,int j)
{
if(X[i]==X[j]) return C[i]>C[j];
return X[i]<X[j];
}
bool cmp2(Query a,Query b)
{
return a.X<b.X;
}
vector<int> A[N*4];
db slope(int i,int j)
{
return (db)(p[j].y-p[i].y)/(p[j].x-p[i].x);
}
void Add(int k,int i)
{
int sz=A[k].size();
while(sz>1&&(p[A[k][sz-1]].y-p[A[k][sz-2]].y)*(p[i].x-p[A[k][sz-1]].x)>=(p[i].y-p[A[k][sz-1]].y)*(p[A[k][sz-1]].x-p[A[k][sz-2]].x))
{
sz--;
A[k].pop_back();
}
A[k].push_back(i);
return;
}
void Insert(int k,int l,int r,int L,int R,int i)
{
if(L<=l&&r<=R)
{
Add(k,i);
return;
}
int mid=(l+r)>>1;
if(L<=mid) Insert(k<<1,l,mid,L,R,i);
if(R>mid) Insert(k<<1|1,mid+1,r,L,R,i);
}
int cur[N*4];
LL Ask(int k,int l,int r,int x,int K)
{
LL res=INF;
int sz=A[k].size();
if(sz)
{
while(cur[k]<sz-1&&p[A[k][cur[k]+1]].y-p[A[k][cur[k]]].y<1ll*K*(p[A[k][cur[k]+1]].x-p[A[k][cur[k]]].x))
{
++cur[k];
}
res=p[A[k][cur[k]]].y-p[A[k][cur[k]]].x*K+1ll*K*K;
}
if(l==r) return res;
int mid=(l+r)>>1;
if (x<=mid) return min(res,Ask(k<<1,l,mid,x,K));
else return min(res,Ask(k<<1|1,mid+1,r,x,K));
}
void Build()
{
for(int i=1;i<=n;i++)
{
int x=id[i];
if(!G[x].empty())
{
p[i]=Point(2ll*X[x],1ll*X[x]*X[x]+C[x]);
int lst=dfn[G[x][0]];
int siz=G[x].size();
for(int j=1;j<siz;j++)
{
int y=G[x][j];
Insert(1,1,n,lst,dfn[y]-1,i);
lst=End[y]+1;
}
if(lst<=End[G[x][0]]) Insert(1,1,n,lst,End[G[x][0]],i);
}
}
}
LL Ans[N];
int main()
{
n=read();
m=read();
C[1]=read();
note[1]=1;
for(int i=2;i<=n;i++)
{
int opt=read(),ex;
if(opt==0)
{
int Fa=read()+1,id=read()+1;
X[id]=read();
ex=read();ex=read();
C[id]=read();
add(Fa,i);
note[i]=id;
}
else
{
int Fa=read()+1,id=read()+1;
add(Fa,i);
note[i]=id;
}
}
dfs(1);
for(int i=1;i<=n;i++) id[i]=i;
sort(id+1,id+n+1,cmp);
Build();
for(int i=1;i<=m;i++)
{
int s=read()+1,x=read();
Q[i]=(Query){x,dfn[s],i};
}
sort(Q+1,Q+m+1,cmp2);
for(int i=1;i<=m;i++)
Ans[Q[i].id]=Ask(1,1,n,Q[i].s,Q[i].X);
for(int i=1;i<=m;i++)
printf("%lld\n",Ans[i]);
return 0;
}