bzoj5077: [Ctsc2016]时空旅行【线段树+凸包】

Description

2045年,人类的技术突飞猛进,已经找到了进行时空旅行的方法。小R得到了一台时空旅行仪,他想用它调查不同
时空中人类的发展状况。根据平行时空理论,宇宙中存在着很多独立的时空,每个时空在下一个时间点还会分化出
若干个不同的时空。宇宙是一个三维空间,人类使用空间直角坐标系来描述空间中的一个位置,三维坐标分别是 x
,y,z。我们假设在初始的时空(编号为 0)中,人类存在于地球上(地球的坐标为 (0,0,0)),其它的时空都是从
一个现有的时空发展而来。一个时空发生一个事件之后会发展成为另外一个时空(原来的时空不发生任何变化)。
会影响小R的事件包括两类:人类殖民了一个新的星球,该星球的状态变成”已被殖民”;人类放弃了一个已被殖民
的星球,该星球的状态变成”未被殖民”。每次进行时空旅行时,小R会先选定一个时空。在这个时空中,人类已经
殖民了一些星球。小R只要到达该时空中任意一个已被殖民的星球,就能调查人类的发展状况。小R的时空旅行仪出
现了一些问题,调整 x 坐标的按钮坏掉了,因此到达点的 x 坐标被固定了(每次旅行的 x 坐标值可能不同)。
与此同时,他仍能任意调整到达点的 y 坐标和 z 坐标。这个问题大大增大了小R的花费:因为时空旅行没有花费
,但在太空中航行却需要花钱;同时,在不同的星球进行调查也可能会产生不同的费用。假设小R将时空旅行的终
点设为 A,他要进行调查的星球为 B:如果 A 与 B 的欧几里德距离为 d,那么他太空航行的花费就为 d^2;又如
果星球 B 上进行调查的费用为 c,那么小R此次调查的总花费就为 d^2+c。现在给定小R每次旅行到达的时空以及
时空旅行仪上固定的 x 坐标值,请你计算出小R每次旅行完成调查的最小总花费。

Input

输入的第一行包含三个非负整数 n,m,c_0,
n 表示平行时空数量,这些平行时空的编号为 0 到 n-1 的整数,初始时空的编号为 0。
m 表示小R进行的时空旅行的次数,
c_0 表示在地球进行调查的花费。保证 n,m 是正整数,0≤c_0≤10^12。
接下来 n-1 行,依次描述平行时空 1 到 n-1,其中第 i 行分两种情况:
0 fr id x y z c:表示编号为 i 的平行时空由编号为 fr 的时空发展而来,数据保证人类殖民了一个编号为 id 的星球,
该星球的坐标为 (x,y,z),在该星球进行调查的花费为 c。
数据保证给出的星球编号不重复,且 0

Output

输出 m 行,分别表示每次旅行完成调查的最小总花费。

Sample Input

4 4 2

0 0 1 8 2 3 7

0 1 2 10 1 6 2

1 1 1

1 4

2 8

2 6

3 8

Sample Output

18

6

11

66

解题思路:

y y z 都是没用的,就是求

min{(xxi)2+ci}=min{2xix+(c+x2i)}+x2 m i n { ( x − x i ) 2 + c i } = m i n { − 2 x i x + ( c + x i 2 ) } + x 2

相当于一个点是一条直线,求 x0=x x 0 = x 时的最小值。

注意有删除操作,肯定要转成插入,注意至多把dfs分成 O(n) O ( n ) 段,还是可以用线段树去维护区间内的凸包,标记永久化,单点查询即可。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
ll getint()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=500005;
int n,m,cnt,id[N],p[N];
int tot,first[N],nxt[N],to[N];
int idx,in[N],out[N];
vector<int>inc[N],del[N];
struct line
{
    ll k,b;int id;
    line(ll _k=0,ll _b=0,int _id=0):k(_k),b(_b),id(_id){}
    inline friend line operator - (const line &a,const line &b){return line(a.k-b.k,a.b-b.b);}
    inline friend bool operator < (const line &a,const line &b){return a.k>b.k;}
    inline friend double operator * (const line &a,const line &b){return (double)a.k*b.b-(double)a.b*b.k;}
    inline ll calc(int x){return k*x+b;}
}L[N];
struct data
{
    int u,x,id;
    inline friend bool operator < (const data &a,const data &b){return a.x<b.x;}
}q[N];
struct node
{
    vector<line>a;int pos,top;
    node(){pos=0,top=-1;}
    void insert(line t)
    {
        if(top>=0&&t.k==a[top].k)
        {
            if(t.b>=a[top].b)return;
            else top--,a.pop_back();
        }
        while(top>=1&&(a[top]-a[top-1])*(t-a[top-1])>=0)
            top--,a.pop_back();
        top++,a.push_back(t);
    }
    ll calc(int x0)
    {
        if(top==-1)return 1e18;
        while(pos<top&&a[pos].calc(x0)>=a[pos+1].calc(x0))pos++;
        return a[pos].calc(x0);
    }
}tr[N<<2];
ll ans[N];
void add(int x,int y)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}
void dfs(int u)
{
    in[u]=++idx;
    id[u]>0?inc[id[u]].pb(idx):del[-id[u]].pb(idx);
    for(int e=first[u];e;e=nxt[e])dfs(to[e]);
    out[u]=idx;
    id[u]>0?del[id[u]].pb(idx+1):inc[-id[u]].pb(idx+1);
}
void modify(int k,int l,int r,int x,int y,line t)
{
    if(x<=l&&r<=y){tr[k].insert(t);return;}
    int mid=l+r>>1;
    if(x<=mid)modify(k<<1,l,mid,x,y,t);
    if(y>mid) modify(k<<1|1,mid+1,r,x,y,t);
}
ll query(int k,int l,int r,int x,int x0)
{
    ll res=tr[k].calc(x0);
    if(l==r)return res;
    int mid=l+r>>1;
    if(x<=mid)return min(res,query(k<<1,l,mid,x,x0));
    else return min(res,query(k<<1|1,mid+1,r,x,x0));
}
int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),m=getint(),L[++cnt]=line(0ll,getint(),1),id[1]=1;
    for(int i=2,op,x;i<=n;i++)
    {
        op=getint(),add(getint()+1,i);
        if(op==1)id[i]=-getint()-1;
        else
        {
            id[i]=getint()+1;
            x=getint(),getint(),getint();
            L[++cnt]=line(-2ll*x,getint()+(ll)x*x,id[i]);
        }
    }
    sort(L+1,L+cnt+1);for(int i=1;i<=cnt;i++)p[L[i].id]=i;
    for(int i=1;i<=n;i++)id[i]=id[i]>0?p[id[i]]:-p[-id[i]];
    dfs(1);
    for(int i=1;i<=cnt;i++)
        for(int j=inc[i].size()-1,l,r;j>=0;j--) 
        {
            l=inc[i][j],r=del[i][j]-1;
            if(l<=r)modify(1,1,n,l,r,L[i]);
        }
    for(int i=1;i<=m;i++)q[i].u=getint()+1,q[i].x=getint(),q[i].id=i;
    sort(q+1,q+m+1);
    for(int i=1;i<=m;i++)ans[q[i].id]=query(1,1,n,in[q[i].u],q[i].x)+(ll)q[i].x*q[i].x;
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值