(简单)线段树 HOJ 2965 Magic-Pen4

Magic-Pen4

My Tags  (Edit)
Source : Darkgt
Time limit : 2 secMemory limit : 64 M

Submitted : 207, Accepted : 57

Darkgt发明了一种强大的魔法笔。当在白色线段上画线时,白色线段就会变成红色;当在红色线段上画线时,红色线段就会变成绿色,当在绿色线段上画线时,绿色线段就会变成黄色……但是颜色的种类是有限的,经过k次改变,终将变为白色。

Input

第一行一个整数T(T<20),表示有T组case,之后每组case第一行输入三个整数:N(1 ≤ N ≤ 100000),k(1 ≤ k ≤ 100)和M(1 ≤ M ≤ 10000)。N表示线段N条单位线段,标号从1开始。k表示一共有k种颜色,接下来有M行输入,表示M条线段。每行包含三个整数P,X,Y。P=0 表示从第X条单位线段到第Y条单位线段之间用魔法笔画线。P=1 表示查询从第X条单位线段到第Y条单位线段之间连续色带的个数。起始线段为白色。

Output

对于每组询问,输出一个结果

Sample Input

1
8 2 8
0 4 3
1 1 5
0 7 8
1 4 8
0 1 1
1 7 1
0 2 7
1 3 6

Sample Output

3
3
5
2

题意:对n条线段进行多次次区间操作,进行一次操作,区间内的线段的颜色+1 如果颜色==k就变为0,然后询问某区间内有多少条色带(几个连续相同颜色的段组成一个色带)
思路:用线段树维护的数据是 一个区间中最左边的颜色和最右边的颜色,以及该区间有多少个色带,还有lazy标记对该区间执行了多少次的操作 每次PushUp的时候,把左右子树的色带加起来,然后如果中间的颜色是相同的就要-1,同时把左边的颜色置为左子树的左边的颜色,右边的颜色置为右子树的右边的颜色
注意的地方:这个题目询问和操作的时候L,R可能会出现R>L的情况,如果出现了这种情况就要转换一下L,R的值

代码:
#include<iostream>
#include<string.h>
#include<cstdio>
#include<stdio.h>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
#define MAX 100000+10
#define MOD 100000000
const int inf = 0x7fffffff;
#define lson l , m , rt<<1
#define rson m+1 , r , rt<<1|1

int add[MAX<<2];
int rcol[MAX<<2];
int lcol[MAX<<2];
int sum[MAX<<2];

int n,m,k;
void PushUp(int rt)
{
if (rcol[rt<<1]==lcol[rt<<1|1])
sum[rt] = sum[rt<<1]+sum[rt<<1|1]-1;
else 
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
rcol[rt] = rcol[rt<<1|1];
lcol[rt] = lcol[rt<<1];
}

void PushDown(int rt)
{
if (add[rt])
{
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];

rcol[rt<<1] = (rcol[rt<<1]+add[rt])%k;
lcol[rt<<1] = (lcol[rt<<1]+add[rt])%k;
rcol[rt<<1|1] = (rcol[rt<<1|1]+add[rt])%k;
lcol[rt<<1|1] = (lcol[rt<<1|1]+add[rt])%k;

add[rt] = 0;
}
}

void Update(int L,int R,int l,int r,int rt)
{
if (L<=l && r<=R)
{
++add[rt];
rcol[rt] = (rcol[rt]+1)%k;
lcol[rt] = (lcol[rt]+1)%k;
return;
}
int m = (l+r)>>1;
PushDown(rt);
if (L<=m) Update(L,R,lson);
if (R>m) Update(L,R,rson);
PushUp(rt);
}

void build(int l,int r,int rt)
{
sum[rt] = 1;
rcol[rt] = 0;
lcol[rt] = 0;
add[rt] = 0;
if (l==r) return;
int m = (l+r)>>1;
build(lson);
build(rson);
}

pair<int,int> ans;
void query(int L,int R,int l,int r,int rt)
{
if (L<=l && r<=R)
{
ans.second += sum[rt];
if (ans.first==lcol[rt])
--ans.second;
ans.first = rcol[rt];
return;
}
int m = (l+r)>>1;
PushDown(rt);
if (L<=m) query(L,R,lson);
if (R>m)  query(L,R,rson);
PushUp(rt);
}

inline void swap(int& x,int& y)
{
int tem = x;
x = y;
y = tem;
}

int main()
{
int T;
cin>>T;
while (T--)
{
scanf("%d%d%d",&n,&k,&m);
build(1,n,1);
int oper,L,R;
while (m--)
{
scanf("%d%d%d",&oper,&L,&R);
if (L>R) swap(L,R);
if (oper==0)
Update(L,R,1,n,1);
else 
{
ans.first = -1;
ans.second = 0;
query(L,R,1,n,1);
printf("%d\n",ans.second);
}
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值