BZOJ 3938 Robot(超哥线段树)

这是一道关于线段树应用的题目,涉及机器人在数轴上的运动和操作。机器人操作包括改变速度方向,而询问则求特定时间点离原点最远机器人的距离。通过建立离散时间线段树,并维护最上方和最下方线段,可以高效解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:一条数轴上有n个机器人,对其进行m次操作。操作t_i commond k_i x_i (1≤k_i≤n)表示ti时刻将第ki个机器人的速度变为正方向上xi格每秒;操作t_i query则是询问ti时刻离原点最远的机器人到原点的距离(t1≤t2≤t3≤…≤tm,若同一时间发生多次操作,则按读入顺序依次执行)

题目链接:BZOJ3938

题解:以时间为x轴,位置坐标为y轴,commond操作相当于在其中加入一段直线,query操作就是询问某一横坐标对应的y的最大值或最小值的绝对值。离散时间建两棵线段树,分别维护最上方和最下方的线段。超哥线段树简单题。

code(第一棵超哥线段树,代码参考大神咯T^T,加了一点注释,算是我自己对超哥线段树(标记永久化?)的理解)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 600005
using namespace std;
inline int read()
{
    char c=getchar(); int num=0,f=1;
    while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
    while (c>='0'&&c<='9') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}
inline long long readl()
{
    char c=getchar(); long long num=0,f=1;
    while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
    while (c>='0'&&c<='9') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}
struct opt{
    int op,id;
    long long k,tm;
}q[N];
long long tagk[N<<2],tagb[N<<2],Tagk[N<<2],Tagb[N<<2];
long long nowk[100005],nowb[100005],b[N],ans1,ans2;
int a[100005],n,m,cnt,num;
bool flag[N<<2],Flag[N<<2];
double cross(long long k1,long long b1,long long k2,long long b2)   //求两条直线(斜截式)的交点的横坐标
{
    return (b1-b2)/(1.0*(k2-k1));
}
void ins_mx(int now,int l,int r,int L,int R,long long B,long long K) //维护最上方的直线
{
    int mid=l+r>>1;
    if (L<=l&&r<=R)
    {
        if (!flag[now])  //是否被直线覆盖过
        {
            tagk[now]=K,tagb[now]=B,flag[now]=true; return; 
        }
        long long f1=B+K*b[l],f2=tagb[now]+tagk[now]*b[l]; //新插入直线和当前覆盖该区间的直线的左端点的值
        long long f3=B+K*b[r],f4=tagb[now]+tagk[now]*b[r];  //新插入直线和当前覆盖该区间的直线的右端点的值
        if (f1<=f2&&f3<=f4) return; //新直线完全在当前直线之下,丢弃
        if (f1>=f2&&f3>=f4) { tagk[now]=K,tagb[now]=B; return; } //新直线完全在当前直线之上,替换
        double xx=cross(K,B,tagk[now],tagb[now]);
        if (f1>=f2)  //交点左侧新直线更高,右侧当前直线更高
        {
            if (xx<=b[mid]) ins_mx(now<<1,l,mid,L,R,B,K);
             else ins_mx(now<<1|1,mid+1,r,L,R,tagb[now],tagk[now]),tagb[now]=B,tagk[now]=K;
             /*在上方时间长的直线保留,短的递归到下一层交点对应的区间*/
        }
        else /*同*/
        {
            if (xx>b[mid]) ins_mx(now<<1|1,mid+1,r,L,R,B,K);
             else ins_mx(now<<1,l,mid,L,R,tagb[now],tagk[now]),tagb[now]=B,tagk[now]=K;
        }
        return;
    }
    if (L<=mid) ins_mx(now<<1,l,mid,L,R,B,K);
    if (R>mid) ins_mx(now<<1|1,mid+1,r,L,R,B,K);
}
void ins_mn(int now,int l,int r,int L,int R,long long B,long long K)
{
    int mid=l+r>>1;
    if (L<=l&&r<=R)
    {
        if (!Flag[now])
        {
            Tagk[now]=K,Tagb[now]=B,Flag[now]=true; return; 
        }
        long long f1=B+K*b[l],f2=Tagb[now]+Tagk[now]*b[l];
        long long f3=B+K*b[r],f4=Tagb[now]+Tagk[now]*b[r];
        if (f1>=f2&&f3>=f4) return;
        if (f1<=f2&&f3<=f4) { Tagk[now]=K,Tagb[now]=B; return; }
        double xx=cross(K,B,Tagk[now],Tagb[now]);
        if (f1<=f2)
        {
            if (xx<=b[mid]) ins_mn(now<<1,l,mid,L,R,B,K);
             else ins_mn(now<<1|1,mid+1,r,L,R,Tagb[now],Tagk[now]),Tagb[now]=B,Tagk[now]=K;
        }
        else
        {
            if (xx>b[mid]) ins_mn(now<<1|1,mid+1,r,L,R,B,K);
             else ins_mn(now<<1,l,mid,L,R,Tagb[now],Tagk[now]),Tagb[now]=B,Tagk[now]=K;
        }
        return;
    }
    if (L<=mid) ins_mn(now<<1,l,mid,L,R,B,K);
    if (R>mid) ins_mn(now<<1|1,mid+1,r,L,R,B,K);
}
void query_mx(int now,int l,int r,int pos) //查询最大值
{
    if (flag[now]) ans1=max(ans1,tagb[now]+tagk[now]*b[pos]); 
    /*线段树中每个区间都对应着一条不同的线段(或者未被完全覆盖),对覆盖pos的log n条线段在pos处的取值取max,即是答案*/
    if (l==r) return;
    int mid=l+r>>1;
    if (pos<=mid) query_mx(now<<1,l,mid,pos);
     else query_mx(now<<1|1,mid+1,r,pos);
}
void query_mn(int now,int l,int r,int pos)
{
    if (Flag[now]) ans2=min(ans2,Tagb[now]+Tagk[now]*b[pos]);
    if (l==r) return;
    int mid=l+r>>1;
    if (pos<=mid) query_mn(now<<1,l,mid,pos);
     else query_mn(now<<1|1,mid+1,r,pos);
}
int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++) nowb[i]=readl(),nowk[i]=0ll,a[i]=0;
    for (int i=1;i<=m;i++)
    {
        b[i]=q[i].tm=readl();
        char s[10]; scanf("%s",s);
        if (s[0]=='c') q[i].id=read(),q[i].k=readl(),q[i].op=1;
         else q[i].op=0;
    }
    num=m; b[++num]=0; sort(b+1,b+num+1); cnt=unique(b+1,b+num+1)-b-1;
    for (int i=1;i<=m;i++)
     if (q[i].op==1)
     {
         int x=q[i].id,lst=lower_bound(b+1,b+cnt+1,a[x])-b;
         int now=lower_bound(b+1,b+cnt+1,q[i].tm)-b;
         ins_mx(1,1,cnt,lst,now,nowb[x],nowk[x]);
         ins_mn(1,1,cnt,lst,now,nowb[x],nowk[x]);
         nowb[x]=nowb[x]+q[i].tm*(nowk[x]-q[i].k);
         nowk[x]=q[i].k; a[x]=q[i].tm;
     }
    for (int i=1;i<=n;i++)
    {
         int lst=lower_bound(b+1,b+cnt+1,a[i])-b;
         ins_mx(1,1,cnt,lst,cnt,nowb[i],nowk[i]);
         ins_mn(1,1,cnt,lst,cnt,nowb[i],nowk[i]);
    }
    for (int i=1;i<=m;i++)
     if (q[i].op==0)
     {
         ans1=ans2=0;
         int now=lower_bound(b+1,b+cnt+1,q[i].tm)-b;
         query_mx(1,1,cnt,now);
         query_mn(1,1,cnt,now);
         printf("%lld\n",max(ans1,-ans2));
     }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值