HOJ2965_Magic-Pen4_解题报告

Brief Description:

给定n条线段以及整数k,线段上有一个颜色值num,现在有2种操作,0,X,Y:将X到Y的线段的(num值+1)%k,1,X,Y:求X到Y被num值划分为多少个部分(连续的相同num值为一个部分)。

Analysis:

这题显然用线段树做,我们根据问题的特点设计线段树节点的域。

线段树节点域为:

struct node{
    int left,right,rcol,lcol,add,sum;
}tt[maxn*4];

LL(idx): idx的左儿子,RR(idx):idx的右儿子

lcol:最左边的点的颜色,  rcol:最右边的点的颜色,add:延迟标记(祖先的加和),sum:本段有多少个部分。

每次Push_up到父节点的时候,tt[idx].sum = tt[LL(idx)].sum + tt[RR(idx)].sum;再判断tt[LL(idx)].rcol与tt[RR(idx)].lcol是否模K同余,若同余,tt[idx].sum--

这个是经典的“区间更新,区间查询”的模型,区间合并的关键细节还是见代码吧:

"区间更新,区间查询",Push_down和Push_up是很关键的操作,Update时的Push_up注意“延迟操作”的更新。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define LL(x) ((x)<<1)
#define RR(x) ((x)<<1|1)

using namespace std;
const int maxn = 100010;
struct node{
    int left,right,rcol,lcol,add,sum;
}tt[maxn*4];
int n,m,mm;

void Build(int left,int right,int idx){
      tt[idx].left = left;
      tt[idx].right = right;
      tt[idx].lcol = tt[idx].rcol = tt[idx].add = 0;
      tt[idx].sum = 1;
      if(left == right) return;
      int mid = (left + right) >> 1;
      Build(left,mid,LL(idx));
      Build(mid+1,right,RR(idx));
}

void Push_up(int idx,int l1,int r1){
    tt[idx].sum = tt[LL(idx)].sum + tt[RR(idx)].sum;
    tt[idx].lcol = tt[LL(idx)].lcol + l1;
    tt[idx].rcol = tt[RR(idx)].rcol + r1;
    if((tt[LL(idx)].rcol+l1-tt[RR(idx)].lcol-r1) % m == 0)
        tt[idx].sum--;
}

void Update(int left,int right,int idx){
     /*important code*/
     if(left<=tt[idx].left && right>=tt[idx].right){
        tt[idx].lcol += (tt[idx].add+1);
        tt[idx].rcol += (tt[idx].add+1);

        // Remember!!
        if(tt[idx].left != tt[idx].right){
            tt[LL(idx)].add += (tt[idx].add+1);
            tt[RR(idx)].add += (tt[idx].add+1);
        }
        tt[idx].add = 0;
        return;
     }
     if(tt[idx].add){ // ясЁы╡ывВ
       tt[LL(idx)].add += tt[idx].add; // remember!!
       tt[RR(idx)].add += tt[idx].add;
       tt[idx].lcol += tt[idx].add;   tt[idx].rcol += tt[idx].add;
       tt[idx].add = 0;
     }
     int mid = (tt[idx].left + tt[idx].right)>>1;
     if(left <= mid){
         Update(left,right,LL(idx));
     }
     if(mid < right){
         Update(left,right,RR(idx));
     }
     /*important code*/
     int l1 = tt[LL(idx)].add,r1 = tt[RR(idx)].add;
     Push_up(idx,l1,r1);
}

int Query(int left,int right,int idx){
    /*important code,保证每次进来的时候确实为正确值*/
    if(tt[idx].add){ //
       if(tt[idx].left != tt[idx].right){
           tt[LL(idx)].add += tt[idx].add; // remember!!
           tt[RR(idx)].add += tt[idx].add;
       }
       tt[idx].lcol += tt[idx].add;   tt[idx].rcol += tt[idx].add;
       tt[idx].add = 0;
    }

    if(left==tt[idx].left && right==tt[idx].right){ //change!!
        return tt[idx].sum;
    }
    int mid = (tt[idx].left + tt[idx].right) >> 1,ret;

    if(right <= mid){
      ret = Query(left,right,LL(idx));
      return ret;
    }else if(left > mid){
      ret = Query(left,right,RR(idx));
      return ret;
    }else{
       ret = Query(left,mid,LL(idx)) + Query(mid+1,right,RR(idx));
       if((tt[LL(idx)].rcol-tt[RR(idx)].lcol) % m == 0)
         ret--;
       return ret;
    }
}
int main()
{
    int cas;
    scanf("%d",&cas);
    while(cas--){
         scanf("%d%d%d",&n,&m,&mm);
         Build(1,n,1);
         while(mm--){
             int key,x,y;
             scanf("%d%d%d",&key,&x,&y);
             if(x > y) swap(x,y);
             if(key == 0)
                Update(x,y,1);
              else printf("%d\n",Query(x,y,1));
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值