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;
}