向前走莫回头❤

【bzoj 3938】Robot(超哥线段树)

3938: Robot

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 141  Solved: 67
[Submit][Status][Discuss]

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

Source

[Submit][Status][Discuss]

【题解】【超哥线段树(标记永久化)】

【这道题,是我做超哥线段树的第二题。。。】

【原本想要按上一道题的思路在每个点插入一条“折线”(因为后面可能更改斜率),然后,当它的速度被更改时,就当做新的一条插入树中并维护最大值,而把它原来的那条线的值改为一个定值(啊,应该改成0啊,怪不得一直WA!),但是在加离散化时,由于是在线操作,搞成一坨。。。】

【看到题解后发现,跟我想的差不多,但题解是离线操作,把所有操作的时间离散,这样可以大大缩短线段树所需的空间,然后分别维护每条直线的最大最小值,每次更新后,就在原先的那条直线上更改,这样就大大简化了编程的复杂度,虽然代码有点长,但是思路很清晰(比我混在一起搞强太多!!!)】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 100010
#define M 600010
using namespace std; 
struct slove{
    ll tm,vx;
    int opt,num;
}a[M];
ll trmx[M*4],lmx[M*4],trmn[M*4],lmn[M*4];//线段树,trmx、lmx分别是维护最大值和最大值时的速度;trmn、lmn分别是维护最小值和最小值时的速度 
ll time[N],tim[M],v[N],len[N];//time是每个点当前状态的开始时间;tim离散化有操作的时间;v[i]表示i点当前的速度;len[i]表示i点当前距原点的距离
bool p1[M*4],p2[M*4];//p1、p2分别表示维护最大值和最小值的线段树中每个节点是否曾更新过
int n,m,cnt;
ll ans1,ans2;
inline double cross(ll v1,ll l1,ll v2,ll l2)
{
    return (l1-l2)/(1.0*(v2-v1));
}
void change_mx(int now,int l,int r,int al,int ar,ll road,ll V)
{
    int mid=(l+r)>>1;
    if(al<=l&&r<=ar) 
      {
        if(!p1[now]) trmx[now]=V,lmx[now]=road,p1[now]=1;
         else
          {
            ll l1=road+V*tim[l],l1_1=lmx[now]+trmx[now]*tim[l];
            ll l2=road+V*tim[r],l2_1=lmx[now]+trmx[now]*tim[r];
            if(l1<=l1_1&&l2<=l2_1) return;
            if(l1>=l1_1&&l2>=l2_1) trmx[now]=V,lmx[now]=road;
             else
              {
                double k=cross(V,road,trmx[now],lmx[now]);
                if(l1>=l1_1)
                  if(k<=tim[mid]) change_mx((now<<1),l,mid,al,ar,road,V);
                    else change_mx((now<<1)|1,mid+1,r,al,ar,lmx[now],trmx[now]),trmx[now]=V,lmx[now]=road;
                   else
                    if(k>tim[mid]) change_mx((now<<1)|1,mid+1,r,al,ar,road,V);
                     else change_mx((now<<1),l,mid,al,ar,lmx[now],trmx[now]),trmx[now]=V,lmx[now]=road; 
               }
          } 
        return;
      }
    if(al<=mid) change_mx((now<<1),l,mid,al,ar,road,V);
    if(ar>mid) change_mx((now<<1)|1,mid+1,r,al,ar,road,V);   
}
void change_mn(int now,int l,int r,int al,int ar,ll road,ll V)
{
    int mid=(l+r)>>1;
    if(al<=l&&r<=ar)
     {
        if(!p2[now]) p2[now]=1,trmn[now]=V,lmn[now]=road;
         else
          {
            ll l1=road+V*tim[l],l1_1=lmn[now]+trmn[now]*tim[l];
            ll l2=road+V*tim[r],l2_1=lmn[now]+trmn[now]*tim[r];
            if(l1>=l1_1&&l2>=l2_1) return;
            if(l1<=l1_1&&l2<=l2_1) trmn[now]=V,lmn[now]=road;
             else
              {
                double k=cross(V,road,trmn[now],lmn[now]);
                if(l1<=l1_1)
                  if(k<=tim[mid]) change_mn((now<<1),l,mid,al,ar,road,V);
                    else change_mn((now<<1)|1,mid+1,r,al,ar,lmn[now],trmn[now]),trmn[now]=V,lmn[now]=road;
                 else
                  if(k>tim[mid]) change_mn((now<<1)|1,mid+1,r,al,ar,road,V);
                   else change_mn((now<<1),l,mid,al,ar,lmn[now],trmn[now]),trmn[now]=V,lmn[now]=road;
              }
          }
        return;
     }
    if(al<=mid) change_mn((now<<1),l,mid,al,ar,road,V);
    if(ar>mid) change_mn((now<<1)|1,mid+1,r,al,ar,road,V);
}
void ask_mx(int now,int l,int r,int x)
{
    if(p1[now]) ans1=max(ans1,lmx[now]+tim[x]*trmx[now]);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) ask_mx(now<<1,l,mid,x);
     else ask_mx((now<<1)|1,mid+1,r,x);
}
void ask_mn(int now,int l,int r,int x)
{
    if(p2[now]) ans2=min(ans2,lmn[now]+tim[x]*trmn[now]);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) ask_mn((now<<1),l,mid,x);
     else ask_mn((now<<1)|1,mid+1,r,x);
}
int main()
{
    //freopen("int.txt","r",stdin);
    //freopen("my.txt","w",stdout);
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i) scanf("%lld",&len[i]),time[i]=0,v[i]=0;
    for(i=1;i<=m;++i)
     {
        scanf("%lld",&a[i].tm); tim[i]=a[i].tm;
        char s[10];
        scanf("%s",s);
        if(s[0]=='c') scanf("%d%lld",&a[i].num,&a[i].vx),a[i].opt=1; 
          else a[i].opt=0;
     }
    cnt=m+1; tim[cnt]=0;
    sort(tim+1,tim+cnt+1);
    cnt=unique(tim+1,tim+cnt+1)-tim-1;//去重,离散化所有可能用到的时间 
    for(i=1;i<=m;++i)
     if(a[i].opt==1)
      {
        int x=a[i].num,lst=lower_bound(tim+1,tim+cnt+1,time[x])-tim,rst=lower_bound(tim+1,tim+cnt+1,a[i].tm)-tim;
        change_mx(1,1,cnt,lst,rst,len[x],v[x]);
        change_mn(1,1,cnt,lst,rst,len[x],v[x]);
        len[x]=len[x]+a[i].tm*(v[x]-a[i].vx); v[x]=a[i].vx; time[x]=a[i].tm;
      }//将操作中能更新到的距离加入线段树 
    for(i=1;i<=n;++i)
     {
        int lst=lower_bound(tim+1,tim+cnt+1,time[i])-tim;
        change_mx(1,1,cnt,lst,cnt,len[i],v[i]);
        change_mn(1,1,cnt,lst,cnt,len[i],v[i]);
     }//将初始的距离与当前的最优值相比较 
    for(i=1;i<=m;++i)
     if(!a[i].opt)
      {
        ans1=ans2=0;
        int now=lower_bound(tim+1,tim+cnt+1,a[i].tm)-tim; 
        ask_mx(1,1,cnt,now); ask_mn(1,1,cnt,now);
        printf("%lld\n",max(ans1,-ans2));
      }//询问,注意因为询问的是距离,那么,最大值和最小值都有可能是答案(这也是保存两个值的原因) 
    return 0;
 } 


posted @ 2016-09-13 18:52  lris0-0  阅读(263)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m