bzoj3938 Robot(超哥线段树)

本文介绍了一个关于机器人在数轴上移动的问题,通过离散化时间并利用线段树来维护和查询机器人位置的方法。该问题涉及到操作命令的执行以及查询离原点最远的机器人位置。

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

Description

小q有n只机器人,一开始他把机器人放在了一条数轴上,第i只机器人在ai的位置上静止,而自己站在原点。在这之后小q会执行一些操作,他想要命令一个机器人向左或者向右移动x格。但是机器人似乎听不清小q的命令,事实上它们会以每秒x格的速度匀速移动。看着自己的机器人越走越远,小q很着急,他想知道当前离他(原点)最远的机器人有多远。具体的操作以及询问见输入格式。注意,不同的机器人之间互不影响,即不用考虑两个机器人撞在了一起的情况。

Input

共有m个事件,输入将会按事件的时间顺序给出。第一行两个正整数n,m。接下来一行n个整数,第i个数是ai,表示第i个机器人初始的位置(初始移动速度为0)。接下来m行,每行行首是一个非负整数ti,表示该事件点发生的时刻(以秒为单位)。第二个是一个字符串S,代表操作的种类。数字与字符串之间用一个空格隔开。接下来的输入按S的种类分类。若S=“command”(不带引号),则接下来两个整数ki,xi,表示小q对第ki个机器人执行了操作,该机器人的速度将会被重置,变为向数轴正方向每秒移动xi格(若xi为负数就相当于向数轴负方向每秒移动∣xi∣格)。保证1≤ki≤n。若S是“query”(不带引号),则你需要输出当前离原点最远的机器人有多远。保证t1≤t2≤t2≤…≤tm。(注:若同一时间发生多次操作,则按读入顺序依次执行)

Output

对于每个query询问,输出一行,包含一个整数表示正确的答案。C/C++输入输出longlong时请用%lld。由于本题数据量较大,建议不要使用cin/cout进行输入输出。

Sample Input

4 5
-20 0 20 100
10 command 1 10
20 command 3 -10
30 query
40 command 1 -30
50 query

Sample Output

180
280

HINT

第一个命令执行时,各个机器人的位置为:−20,0,20,100。
第二个命令执行时,各个机器人的位置为:80,0,20,100。
第一个询问时,各个机器人的位置为:180,0,−80,100。
第三个命令执行时,各个机器人的位置为:280,0,−180,100。
第二个询问时,各个机器人的位置为:−20,0,−280,100。

限制与约定 设 command 的个数为 C,query 的个数为 Q。(所以 C+Q=m)
对于所有的事件满足 0≤ti≤10^9,对于所有的 command 满足 ∣xi∣≤10^4。
对于所有的机器人满足 ∣ai∣≤10^9。 N,C<=10^5 Q<=5*10^5



分析: 我们可以把时间建成线段树 首先把时间离散一下 在超哥线段树中插入线段即可 因为我们最后查询的是绝对值最大的位置 因此我们在线段树中要维护**最上方和最下方的线段** 标记永久化单点查询即可

tip

每个结点都设置一个覆盖标记
如果从来没有线段覆盖过,在初次覆盖的时候直接看一下标记即可
查询的时候也可以通过判断标记防止答案错误
注意在判断交点在哪一个区间的时候,不要忘了线段树的区间是离散后的时间

if (x<=ti[mid]) 

啊啊啊啊啊。。。。写了一下午+一晚上
一开始没有想明白离散化
后来发现没有看见是绝对值最大
代码量超长180+

在bzoj上一直T
发现是在线段树中没有及时return:

if (r<l) return;

终于变成了WA
本来是想借鉴一下前辈的代码,最后只能找不同了。。。
在处理的时候,我们添加一个时刻0,方便计算直线的斜率和截距
插入线段时候,我们把每一个机器人按照操作顺序分成若干段分别插入
数据应该是保证时间是单调递增的

这道题虽然是自己想出来的,但是调了好久。。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

const int INF=1e9;
const int N=600005;
struct node{
    ll mxa,mxb,mna,mnb;
};
node T[N<<2];
int n,m,nn;
struct point{
    int type;
    ll x,y,tm;
};
point po[N];
ll ti[N],A[N],maxx,minn,nowk[N],nowb[N];
bool f1[N<<2],f2[N<<2];

void change_max(int bh,int l,int r,int L,int R,ll K,ll B)
{
    if (r<l) return;
    int mid=(l+r)>>1;
    if (l>=L&&r<=R)
    {
        if (!f1[bh])
        {
            T[bh].mxa=K; T[bh].mxb=B; f1[bh]=1;
            return;
        }

        ll z1=T[bh].mxa*ti[l]+T[bh].mxb;   //原线段 
        ll z2=T[bh].mxa*ti[r]+T[bh].mxb;
        ll z3=K*ti[l]+B;
        ll z4=K*ti[r]+B;

        if (z1>=z3&&z2>=z4) return;
        if (z1<=z3&&z2<=z4)
        {
            T[bh].mxa=K; T[bh].mxb=B;
            return;
        }
        double x=(double)(T[bh].mxb-B)/(K-T[bh].mxa);
        if (x<=ti[mid])                              //对比的是原时间 
        {
            if (z1<z3) change_max(bh<<1,l,mid,L,R,K,B);
            else {
                change_max(bh<<1,l,mid,L,R,T[bh].mxa,T[bh].mxb);
                T[bh].mxa=K; T[bh].mxb=B;
            }
        }
        else
        {
            if (z1>z3) change_max((bh<<1)+1,mid+1,r,L,R,K,B);
            else{
                change_max((bh<<1)+1,mid+1,r,L,R,T[bh].mxa,T[bh].mxb);
                T[bh].mxa=K; T[bh].mxb=B;
            }
        }
        return;
    }
    if (L<=mid) change_max(bh<<1,l,mid,L,R,K,B);
    if (R>mid) change_max((bh<<1)+1,mid+1,r,L,R,K,B);
}

void change_min(int bh,int l,int r,int L,int R,ll K,ll B)
{
    if (r<l) return;
    int mid=(l+r)>>1;
    if (l>=L&&r<=R)
    {
        if (!f2[bh])
        {
            T[bh].mna=K; T[bh].mnb=B; f2[bh]=1;
            return;
        }

        ll z1=T[bh].mna*ti[l]+T[bh].mnb;   //原线段 
        ll z2=T[bh].mna*ti[r]+T[bh].mnb;
        ll z3=K*ti[l]+B;
        ll z4=K*ti[r]+B;

        if (z1<=z3&&z2<=z4) return;
        if (z1>=z3&&z2>=z4)
        {
            T[bh].mna=K; T[bh].mnb=B;
            return;
        }
        double x=(double)(T[bh].mnb-B)/(K-T[bh].mna);
        if (x<=ti[mid])                              //对比的是原时间 
        {
            if (z1>z3) change_min(bh<<1,l,mid,L,R,K,B);
            else {
                change_min(bh<<1,l,mid,L,R,T[bh].mna,T[bh].mnb);
                T[bh].mna=K; T[bh].mnb=B;
            }
        }
        else
        {
            if (z1<z3) change_min((bh<<1)+1,mid+1,r,L,R,K,B);
            else{
                change_min((bh<<1)+1,mid+1,r,L,R,T[bh].mna,T[bh].mnb);
                T[bh].mna=K; T[bh].mnb=B;
            }
        }
        return;
    }
    if (L<=mid) change_min(bh<<1,l,mid,L,R,K,B);
    if (R>mid) change_min((bh<<1)+1,mid+1,r,L,R,K,B);
}

void ask(int bh,int l,int r,int pos)
{
    if (r<l) return;
    if (f1[bh]) maxx=max(maxx,T[bh].mxa*ti[pos]+T[bh].mxb);      //一路上都要维护 
    if (f2[bh]) minn=min(minn,T[bh].mna*ti[pos]+T[bh].mnb); 
    if (l==r) return;
    int mid=(l+r)>>1;
    if (pos<=mid) ask(bh<<1,l,mid,pos);
    else ask((bh<<1)+1,mid+1,r,pos);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&nowb[i]),nowk[i]=0,A[i]=0;
    char s[20];
    for (int i=1;i<=m;i++)
    {
        scanf("%lld",&po[i].tm); ti[i]=po[i].tm;
        scanf("%s",s);
        if (s[0]=='c')
        {
            po[i].type=1;
            scanf("%lld%lld",&po[i].x,&po[i].y); 
        }
        else po[i].type=2;
    }

    int num=m;
    ti[++num]=0;                  //添加一个初试时刻0 
    sort(ti+1,ti+1+num);          //离散化时间 
    nn=unique(ti+1,ti+1+num)-ti-1;

    for (int i=1;i<=m;i++) if (po[i].type==1)
    {
        int x=po[i].x;
        int lst=lower_bound(ti+1,ti+1+nn,A[x])-ti;          //编号x上一次修改的时间 
        int now=lower_bound(ti+1,ti+1+nn,po[i].tm)-ti;      //编号x这次修改的时间
        change_max(1,1,nn,lst,now,nowk[x],nowb[x]); 
        change_min(1,1,nn,lst,now,nowk[x],nowb[x]); 
        nowb[x]=po[i].tm*(nowk[x]-po[i].y)+nowb[x]; 
        nowk[x]=po[i].y; A[x]=po[i].tm; 
    } 
    for (int i=1;i<=n;i++)
    {
        int now=lower_bound(ti+1,ti+1+nn,A[i])-ti;
        change_max(1,1,nn,now,nn,nowk[i],nowb[i]); 
        change_min(1,1,nn,now,nn,nowk[i],nowb[i]); 
    }

    for (int i=1;i<=m;i++) if (po[i].type==2)
    {
        maxx=0; minn=0;
        int now=lower_bound(ti+1,ti+1+nn,po[i].tm)-ti;
        ask(1,1,nn,now);
        printf("%lld\n",max(maxx,-minn));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值