洛谷 P5416 [CTSC2016] 时空旅行
洛谷 P5416 [CTSC2016] 时空旅行
https://www.luogu.com.cn/problem/P5416
用三维坐标描述宇宙中各个星球的位置,初始的编号为 \(0\) 的时空中,人类存在与地球 \((0,0,0)\) 上,其他的时空都是由一个现有的时空发展而来,一个时空在发生了一个事件后会发展成为另一个时空(原来的时空不发生变化).第 \(i\) 个时空可以描述为以下两种形式之一
-
\(fr,id,x,y,z,c\) ,表示第 \(i\) 个时空由 \(fr\) 号时空发展而来,人类殖民了编号为 \(id\) 的星球,它的坐标是 \((x,y,z)\) ,在这个星球上调查的花费为 \(c\) .
保证 \(id\) 两两不同且 \(0 < id < n\) , \(0 \le fr < i, |x|,|y|,|z| \le 10^6, 0 \le c \le 10^{12}\) .
-
\(1,fr,id\) ,表示第 \(i\) 个时空由 \(fr\) 号时空发展而来,人类放弃了编号为 \(id\) 的星球,保证编号为 \(fr\) 的时空中 \(id\) 星球处于被殖民状态,且 \(id>0\) . \(0 \le fr<i\)
小R要在进行 \(m\) 次调查,第 \(i\) 次调查会前往 \(s_i\) 号时空,并传送到 \(x\) 坐标为 \(x_i\) 的某个点上,然后选择一个星球进行调查,一次调查的花费为 \(d^2+c\) ,其中 \(d\) 为传送位置与星球坐标之间的欧几里得距离, \(c\) 为选择的星球的调查费用.求每次调查的最小花费.
\(n,m \le 5 \times 10^5\)
Tutorial
https://www.cnblogs.com/Tiw-Air-OAO/p/12408531.html
显然, \(y,z\) 不影响答案,所以花费可以表示为 \((x-x_i)^2+c=-2x_ix+x_i^2+x^2+c\) ,也就是斜率优化的形式.
考虑时空的树形结构上,一个星球的贡献范围可以表示为一个子树的根节点开始,不经过那些表示删除掉这个星球的节点的连通块.将它表示在dfs序上.假如这个星球被删除了 \(x\) 次,那么就会产生 \(x+1\) 个区间,也就是说总区间数是 \(O(n)\) 的.
使用线段树分治,在外部对斜率排序,时间复杂度 \(O(n \log n)\)
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define lson u<<1,l,mid
#define rson u<<1|1,mid+1,r
#define fi first
#define se second
#define SZ q[u].size()
using namespace std;
inline char nc()
{
// return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x)
{
x=0; int f=1,ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
x*=f;
}
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1e18;
const int maxn=5e5+50;
const int maxm=5e5+50;
int n,m;
int x[maxn]; ll c[maxn];
int s[maxm],x0[maxm];
int r[maxn]; bool op[maxn];
ll an[maxm];
int p;
int dfc;
int ed[maxn];
int st[maxn];
int ord[maxn];
int pos[maxn];
int head[maxn];
vector<int> rec[maxn];
vector<pii> range;
struct edge
{
int to,nex;
edge(int to=0,int nex=0):to(to),nex(nex){}
};
vector<edge> G;
inline void addedge(int u,int v)
{
G.push_back(edge(v,head[u])),head[u]=G.size()-1;
}
inline bool invalid(int u,int v,int w)
{
return (c[v]-c[u])*(x[w]-x[v])>=(c[w]-c[v])*(x[v]-x[u]);
}
namespace seg
{
const int maxnode=maxn<<2;
int pt[maxnode];
vector<int> q[maxnode];
void update(int u,int l,int r,int ql,int qr,int qv)
{
if(l==ql&&r==qr)
{
if(SZ&&x[q[u][SZ-1]]==x[qv]) return;
while(SZ>1&&invalid(q[u][SZ-2],q[u][SZ-1],qv)) q[u].pop_back();
q[u].push_back(qv);
return;
}
int mid=(l+r)>>1;
if(qr<=mid) update(lson,ql,qr,qv);
else if(ql>mid) update(rson,ql,qr,qv);
else
{
update(lson,ql,mid,qv);
update(rson,mid+1,qr,qv);
}
}
inline bool Cmin(ll &x,ll y) {return x>y?x=y,1:0;}
ll query(int u,int l,int r,int qp,int x0)
{
ll re=inf;
while(pt[u]+1<SZ&&x0*2ll*(x[q[u][pt[u]+1]]-x[q[u][pt[u]]])>=(c[q[u][pt[u]+1]]-c[q[u][pt[u]]])) ++pt[u];
if(SZ)
{
re=(ll)x0*x0-2ll*x0*x[q[u][pt[u]]]+c[q[u][pt[u]]];
}
if(l==r)
{
return re;
}
int mid=(l+r)>>1;
if(qp<=mid) return min(re,query(lson,qp,x0));
else return min(re,query(rson,qp,x0));
}
}
void dfs(int u)
{
st[u]=++dfc;
for(int i=head[u];~i;i=G[i].nex)
{
int v=G[i].to;
dfs(v);
}
ed[u]=dfc;
}
inline int cmp0(const int &a,const int &b)
{
if(x[a]!=x[b]) return x[a]<x[b];
return c[a]<c[b];
}
inline int cmp1(const int &a,const int &b)
{
return st[a]<st[b];
}
inline int cmp2(const int &a,const int &b)
{
return x0[a]<x0[b];
}
int main()
{
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
read(n),read(m),read(c[0]);
memset(pos,-1,sizeof(pos)),pos[0]=0;
memset(head,-1,sizeof(head));
for(int i=1;i<n;++i)
{
read(op[i]);
if(op[i]==0)
{
int fr,y,z; read(fr),read(r[i]),read(x[r[i]]),read(y),read(z),read(c[r[i]]);
c[r[i]]+=(ll)x[r[i]]*x[r[i]];
pos[r[i]]=i;
addedge(fr,i);
}
else
{
int fr; read(fr),read(r[i]);
rec[r[i]].push_back(i);
addedge(fr,i);
}
}
dfs(0);
for(int i=0;i<=n;++i) if(pos[i]!=-1)
{
ord[++p]=i;
}
sort(ord+1,ord+p+1,cmp0);
int cnt=0;
for(int i=1;i<=p;++i)
{
int x=ord[i];
range.clear();
int now=st[pos[x]];
if(rec[x].size()) sort(rec[x].begin(),rec[x].end(),cmp1);
for(int j=0;j<rec[x].size();++j)
{
int u=rec[x][j];
if(now<st[u]) range.push_back(make_pair(now,st[u]-1));
now=ed[u]+1;
}
if(now<=ed[pos[x]]) range.push_back(make_pair(now,ed[pos[x]]));
for(int j=0;j<range.size();++j)
{
int l=range[j].fi,r=range[j].se;
// debug("%d %d %d %lld\n",l,r,::x[x],c[x]);
seg::update(1,1,n,l,r,x);
++cnt;
}
}
// debug("%d\n",cnt);
for(int i=1;i<=m;++i)
{
ord[i]=i;
read(s[i]),read(x0[i]);
}
sort(ord+1,ord+m+1,cmp2);
for(int i=1;i<=m;++i)
{
int u=ord[i];
an[u]=seg::query(1,1,n,st[s[u]],x0[u]);
}
for(int i=1;i<=m;++i)
{
printf("%lld\n",an[i]);
}
return 0;
}