【uoj】198:【CTSC2016】时空旅行-dfs序&线段树&凸包

传送门:uoj198


题解

y,z坐标无用。
先化简一下式子,假设选择的是第 i i 个星球,其x轴上值为 xi x i 。则花费为:
(xix0)2+ci=2xix0+x2i+ci+x20 ( x i − x 0 ) 2 + c i = − 2 x i x 0 + x i 2 + c i + x 0 2
不考虑 x0 x 0 ,这个式子就是个直线,所以我们维护一下凸包就好了。
斜率 K=2xi K = − 2 x i ,截距 B=x2i+ci B = x i 2 + c i ,按 K K 第一关键字降序,B第二关键字升序sort一遍,加入凸包就好了(上凸包)。
鉴于有时间轴的限制,我们来考虑每个操作。
在没有删去操作的前提下:
对于添加一个星球,这条直线会被添加到以该结点为根的子树中的所有结点中。
我们给每个点标两个端点,查询的时候维护一下单调队列。
那么有修改操作时,删除一条直线显然不好做,那么可以知道,在该星球存在的子树中,若某次执行了删除操作,该节点子树内就都没有这条直线了。又可知我们可以把有该直线的结点用dfs序分成一个个区间,我们用线段树对每个区间加一下就好了。
加的时候我们并不需要传递下标,因为这样一条条直线又下传实际上更不好操作,所以直接加在线段树的每个区间节点上,查询的时候直接一层一层推上去,求个min就好了。
我们查询当然是离线下来,按 x x 从小到大线性求凸包。
此题代码细节特别多,注意时空节点和星球标号之间的关系,tag数组我就debug了1h,比如c的范围令人窒息,严格来说斜率xi2+ci要是数据开满都到 224 2 24 了,还有空间限制,WA,RE了好多次……幸而终于调出来了。


代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+11;
const int M=5e6+11;
const ll inf=1e17;
int n,m,toa,tob;
int num,st[M],ed[M],pos[N];
int tag[N],cnt,q[N],df[N],ot[N],sz[M];
int ask[N],h[M],dfn;
ll a[N],b[N],ans[N],sit[N];

inline ll rd()
{
    char ch=getchar();ll x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}

struct L{
    int tot,head[N],to[N],nxt[N];
    void lk(int u,int v)
    {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
}ori,orz;

inline bool cmp0(const int& x,const int& y)
{return df[x]<df[y];}
inline bool cmp1(const int& x,const int& y)
{return (a[x]==a[y])? b[x]<b[y]:a[x]>a[y];}
inline bool cmp2(const int& x,const int& y)
{return sit[x]<sit[y];}
inline bool cal(int x,int y,int z)
{return (a[y]-a[x])*(b[z]-b[x])>=(b[y]-b[x])*(a[z]-a[x]);}
inline ll min(ll x,ll y){return x>y?y:x;}
inline void dfs(int x)
{
    df[x]=++dfn;
    for(int i=ori.head[x];i;i=ori.nxt[i]) dfs(ori.to[i]);
    ot[x]=dfn;
}

inline void ad(int k,int l,int r,int L,int R)
{
    if(l>=L &&  r<=R){sz[k]++;return;}
    int mid=(l+r)>>1;
    if(L<=mid) ad(k<<1,l,mid,L,R);
    if(R>mid) ad(k<<1|1,mid+1,r,L,R);
}

inline void ins(int k,int l,int r,int L,int R,int z)
{
    if(l>=L && r<=R){
        int &i=ed[k];
        while(st[k]<i && cal(h[i-1],h[i],z)) i--;
        h[++i]=z;
    }else{
        int mid=(l+r)>>1;
        if(L<=mid) ins(k<<1,l,mid,L,R,z);
        if(R>mid) ins(k<<1|1,mid+1,r,L,R,z);
    }
}

inline void build(int k,int l,int r)
{
    st[k]=num+1;ed[k]=num;num+=sz[k];
    if(l==r){pos[l]=k;return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}

inline ll get(int k,int x)
{
    ll as=inf;
    for(k=pos[k];k;k>>=1){
        int &i=st[k];
        while(i<ed[k] && 1ll*(a[h[i+1]]-a[h[i]])*x<=b[h[i]]-b[h[i+1]]) i++;
        if(i<=ed[k]) as=min(as,1ll*a[h[i]]*x+b[h[i]]);
    }
    return as;
}

int main(){
    scanf("%d%d",&n,&m);b[1]=rd();tag[1]=1;
    for(int op,i=2;i<=n;i++){
        scanf("%d",&op);
        int fr=rd()+1,id=rd()+1;ori.lk(fr,i);
        if(op) orz.lk(id,i);else{
            tag[id]=i;
            a[id]=rd();rd();rd();b[id]=rd();
            b[id]+=(ll)a[id]*a[id];a[id]=-(ll)a[id]*2;
        }
    }
    dfs(1);
    for(int k,j,i=1;i<=n;i++)if(tag[i]){
        for(num=0,j=orz.head[i];j;j=orz.nxt[j]) q[++num]=orz.to[j];
        sort(q+1,q+num+1,cmp0);
        for(k=df[tag[i]],num=0,j=orz.head[i];j;j=orz.nxt[j]){
            orz.to[j]=q[++num];
            if(k<df[q[num]]) ad(1,1,n,k,df[q[num]]-1);
            k=ot[q[num]]+1;
        }
        if(k<=ot[tag[i]]) ad(1,1,n,k,ot[tag[i]]);
    }
    num=0;build(1,1,n);
    for(int i=1;i<=n;i++) q[i]=i;
    sort(q+1,q+n+1,cmp1);
    for(int i,j,k,l=1;l<=n;l++) if(tag[i=q[l]]){
        for(k=df[tag[i]],j=orz.head[i];j;j=orz.nxt[j]){
            if(k<df[orz.to[j]]) ins(1,1,n,k,df[orz.to[j]]-1,i);
            k=ot[orz.to[j]]+1;
        }
        if(k<=ot[tag[i]]) ins(1,1,n,k,ot[tag[i]],i);
    }
    for(int tp,i=1;i<=m;i++){
        q[i]=i;scanf("%d",&tp);
        ask[i]=df[tp+1];sit[i]=rd();
    }
    sort(q+1,q+m+1,cmp2);
    for(int i,l=1;l<=m;l++){i=q[l];ans[i]=get(ask[i],sit[i])+(ll)sit[i]*sit[i];}
    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、付费专栏及课程。

余额充值