[CTSC2016] 时空旅行 题解
好久没有做过这么巧妙的题目了,开心捏O(∩_∩)O~
由于传送的 \(y,z\) 坐标自选,所以实际上对于一个点给出的坐标 \((a_i,b_i,c_i)\) 以及这个点调查的花费 \(cs_i\),我们只需要维护 \(a_i,cs_i\) 即可。那么设规定的 \(x\) 值为 \(x_0\),那么到该点调查的花费即为 \((x_0-a_i)^2+c_i=x_0^2-2x_0a_i+a_i^2+c_i\)。
那么根据凸壳经典推式子方法,我们得到(下设 \(a_i>a_j,d_i=a_i^2+c_i\)):
经典下凸壳公式。这样我们就可以通过维护下凸壳后二分来得到最优解。
那么问题来了:我们该如何维护删除操作呢?
显然,所有时空可以构成一棵树,每一条边 \(u\to v\) 表示 \(v\) 是由 \(u\) 转化而来的。我们容易证明,这棵树假如我们按照 \(dfs\) 序进行排序,那么连续的操作段一定只有 \(n\) 段。证明是显然的,当我们进入一棵子树并加入一个点的时候,我们添加了一个操作段;当我们进入一棵子树并删除一个点的时候,我们断开了一个操作段,操作段数量 \(+1\)。
多条操作段,极其容易想到线段树分治。我们可以给线段树上每个区间都建立一个下凸壳,查询时对经过的所有区间都求一遍最小值即可。假如事先不进行排序,那么时间复杂度就是 \(O(n\log^2n)\) 的,考虑优化。
插入时,时间复杂度之所以是 \(\log^2n\),是因为我们还要对所有点进行一次排序。假如我们在插入前就进行排序,就可以省掉线段树内部的排序。
查询时,时间复杂度之所以是 \(\log^2n\),是因为我们要用二分找答案。那假如我们根据 \(x_0\) 的值事先对询问进行排序,我们就可以用均摊 \(O(\log n)\) 的算法解决。我们可以记录每一个区间上一次被访问时的答案位置。根据单调性,这一次的答案一定在上一次的答案之后。由于所有的区间的凸包总共只有 \(n\log n\) 级别个点,所以可以保证时间复杂度正确性。
这样,我们就将时间复杂度优化到了 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
struct adm{int l,r,id;}ad[N];
struct que{int s,x,id;}qu[N];
int n,m,dfn[N];vector<int>g[N];
int id,a[N],d[N],chg[N],ans[N];
vector<int>lf[N],rt[N];int sum;
int cmp(adm x,adm y){
return a[x.id]<a[y.id];
}int cmq(que x,que y){
return x.x<y.x;
}void dfs(int x){
dfn[x]=++id;
if(chg[x]>0) lf[chg[x]].push_back(id);
else rt[-chg[x]].push_back(id-1);
for(auto y:g[x]) dfs(y);
if(chg[x]>0) rt[chg[x]].push_back(id);
else lf[-chg[x]].push_back(id+1);
}namespace SGT{
vector<int>st[N*4];int tp[N*4];
int check(int x,int y,int z){
return (d[z]-d[y])*(a[y]-a[x])<=(d[y]-d[x])*(a[z]-a[y]);
}int gta(int id,int x){
return d[id]-2*x*a[id]+x*x;
}void add(int x,int l,int r,int L,int R,int id){
if(L>R) return;
if(L<=l&&r<=R){
int t=st[x].size()-1;
while(t>0&&check(st[x][t-1],st[x][t],id)) t--,st[x].pop_back();
return st[x].push_back(id);
}int mid=(l+r)/2;
if(L<=mid) add(x*2,l,mid,L,R,id);
if(R>mid) add(x*2+1,mid+1,r,L,R,id);
}int ask(int x,int l,int r,int k,int xa){
int &nw=tp[x],t=st[x].size()-1,mid=(l+r)/2,as=1e18;
while(nw<t&>a(st[x][nw],xa)>gta(st[x][nw+1],xa)) nw++;
if(t>-1) as=gta(st[x][nw],xa);
if(l==r) return as;
if(k<=mid) return min(as,ask(x*2,l,mid,k,xa));
return min(as,ask(x*2+1,mid+1,r,k,xa));
}
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>d[1],chg[1]=1;
for(int i=2;i<=n;i++){
int opt,s,id;cin>>opt>>s>>id,id++;
g[++s].push_back(i),chg[i]=-id;
if(opt) continue;
cin>>a[id]>>s>>s>>d[id];
d[id]+=a[id]*a[id],chg[i]=id;
}dfs(1);
for(int i=1;i<=n;i++)
for(int j=0;j<lf[i].size();j++)
ad[++sum]={lf[i][j],rt[i][j],i};
sort(ad+1,ad+sum+1,cmp);
for(int i=1;i<=m;i++)
cin>>qu[i].s>>qu[i].x,qu[i].s++,qu[i].id=i;
sort(qu+1,qu+m+1,cmq);
for(int i=1;i<=sum;i++)
SGT::add(1,1,n,ad[i].l,ad[i].r,ad[i].id);
for(int i=1;i<=m;i++)
ans[qu[i].id]=SGT::ask(1,1,n,dfn[qu[i].s],qu[i].x);
for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
return 0;
}

浙公网安备 33010602011771号