solution-cf817f

CF817F题解

posted on 2022-01-08 08:09:05 | under 题解 | source

题面

本题看完后我们发现它应该需要维护 3 种操作:

  • 将 l 到 r 的区间用 1 覆盖

  • 将 l 到 r 的区间用 0 覆盖

  • 将 l 到 r 的区间取反

区间的初始值都为 0。

在每次操作后输出第一个 0 的下标。

对于区间修改的问题,很明显是线段树。

只不过。。。细节多得让人疯

1. 对于懒惰标记,我们有 4 种情况:

  • 0:表示没有标记

  • 1:表示要用 0 覆盖

  • 2:表示要用 1 覆盖

  • 3:表示要取反

所以,下降懒惰标记也。。。

  void down(int q){
      if(c[q].p&&c[q].l!=c[q].r){//有标记
          int l=q<<1,r=q<<1|1;
          if(c[q].p!=3){//这个懒惰标记是覆盖
              c[l].p=c[q].p;c[l].v=(c[q].p-1)*(c[l].r-c[l].l+1);
              c[r].p=c[q].p;c[r].v=(c[q].p-1)*(c[r].r-c[r].l+1);//那就直接覆盖
          }
          else{//这个标记是反转
              if(c[l].p==0)c[l].v=c[l].r-c[l].l+1-c[l].v,c[l].p=3;//没有就直接修改
              else if(c[l].p==1)c[l].v=c[l].r-c[l].l+1,c[l].p=2;//如果用0覆盖,就换成用1覆盖
              else if(c[l].p==2)c[l].v=0,c[l].p=1;//如果用1覆盖,就换成用0覆盖
              else c[l].v=c[l].r-c[l].l+1-c[l].v,c[l].p=0;//如果标记了反转,直接改
              if(c[r].p==0)c[r].v=c[r].r-c[r].l+1-c[r].v,c[r].p=3;
              else if(c[r].p==1)c[r].v=c[r].r-c[r].l+1,c[r].p=2;
              else if(c[r].p==2)c[r].v=0,c[r].p=1;
              else c[r].v=c[r].r-c[r].l+1-c[r].v,c[r].p=0;//同理
          }
      }
      c[q].p=0;
  }

2.对于区间修改,我们有两种:

1.覆盖

  void Add(int q,int l,int r,int x){
          if(l<=c[q].l&&c[q].r<=r){c[q].p=x;c[q].v=(x-1)*(c[q].r-c[q].l+1);return;}//修改整个区间,直接改懒惰标记
          down(q);
          int mid=c[q].l+c[q].r>>1;
          if(l<=mid)Add(q<<1,l,r,x);
          if(mid<r)Add(q<<1|1,l,r,x);
          c[q].v=c[q<<1].v+c[q<<1|1].v;
   }

2.反转

  void Swap(int q,int l,int r){
      if(l<=c[q].l&&c[q].r<=r){//反转整个区间
          if(c[q].p==1||c[q].p==2){//原本是覆盖,那就反着覆盖
              c[q].v=c[q].r-c[q].l+1-c[q].v;
              c[q].p=(c[q].p==1?2:1);
          }
          else{//原本是反转或没有,直接改
              c[q].v=c[q].r-c[q].l+1-c[q].v;
              c[q].p=(c[q].p==0?3:0);
          }
          return;
      }
      down(q);
      int mid=c[q].l+c[q].r>>1;
      if(l<=mid)Swap(q<<1,l,r);
      if(mid<r)Swap(q<<1|1,l,r);
      c[q].v=c[q<<1].v+c[q<<1|1].v;
  }

3.查询

  int Gets(int q){
      if(c[q].v==0)return c[q].l;//如果这个区间都是0,返回左端点
      if(c[q].v==c[q].r-c[q].l+1)return 0;//如果都是1,无解
      down(q);//下传
      int sum=Gets(q<<1);//先左后右
      if(sum!=0)return sum;
      sum=Gets(q<<1|1);
      return sum;
  }

4.主函数

  scanf("%lld",&n);
  for(int i=1;i<=n;i++){
      scanf("%lld%lld%lld",&a[i].t,&a[i].l,&a[i].r);
      p[(i-1)*4+1]=a[i].l;
      p[(i-1)*4+2]=a[i].l-1;//加入l-1(加入更优的答案)
      p[(i-1)*4+3]=a[i].r;
      p[i*4]=a[i].r+1;//加入r+1保证有答案
  }
  p[n*4+1]=1;//一定要有1!!!
  sort(p+1,p+n*4+2);
  for(int i=1;i<=n*4+1;i++){if(p[i]!=p[i-1])M_A[p[i]]=++tot,M_B[tot]=p[i];}//离散化
  T.Build(1,1,tot);
  for(int i=1;i<=n;i++){
      if(a[i].t!=3)T.Add(1,M_A[a[i].l],M_A[a[i].r],(a[i].t==1?2:1));
      else T.Swap(1,M_A[a[i].l],M_A[a[i].r]);//按要求操作
      printf("%lld\n",M_B[T.Gets(1)]);//输出
  }

这是总代码(无注释)

posted @ 2025-02-25 15:31  Grisses  阅读(18)  评论(0)    收藏  举报
Document