问题描述
校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,读入l,r表示在l~r之间种上的一种树
K=2,读入l,r表示询问l~r之间能见到多少种树
(0<l,r)
输入格式
第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作
输出格式
对于每个k=2输出一个答案
样例输入
5 4
1 1 3
2 2 5
1 2 4
2 3 5
样例输出
1
2
数据范围
20%的数据保证,n,m<=100
60%的数据保证,n <=1000,m<=50000
100%的数据保证,n,m<=50000
题解
cnt1[]记录下某点及以前共出现了多少种树,
cnt2[]记录某点及以前共有多少段完整的区间。
因此询问(x,y)的答案为cnt1[y]-cnt2[x-1]
对于修改操作,将cnt1在[l,∞)内的数全部加一,将cnt2在[r,∞)内的数全部加一。
用线段树进行区间修改。具体见代码(我用的动态开点)
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int tot=2,cnt[500000],ls[500000],rs[500000],maxr,lazy[500000];
void putdown(int x)
{
if(!ls[x])ls[x]=++tot;
if(!rs[x])rs[x]=++tot;
int v=lazy[x];
cnt[ls[x]]+=v;
cnt[rs[x]]+=v;
lazy[ls[x]]+=v;
lazy[rs[x]]+=v;
lazy[x]=0;
}
void xiu(int now,int l,int r,int x,int y)
{
if(lazy[now])putdown(now);
if(x<=l&&y>=r){
cnt[now]++;
lazy[now]++;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
{
if(!ls[now])ls[now]=++tot;
xiu(ls[now],l,mid,x,y);
}
if(y>mid)
{
if(!rs[now])rs[now]=++tot;
xiu(rs[now],mid+1,r,x,y);
}
}
int get(int now,int l,int r,int x)
{
if(l==r)return cnt[now];
if(lazy[now])putdown(now);
int mid=(l+r)>>1;
if(x<=mid)return get(ls[now],l,mid,x);
else return get(rs[now],mid+1,r,x);
}
int main()
{
int i,m,x,y;
scanf("%d%d",&maxr,&m);
while(m--)
{
scanf("%d%d%d",&i,&x,&y);
if(i==1)xiu(1,0,maxr,x,maxr),xiu(2,0,maxr,y,maxr);
else printf("%d\n",get(1,0,maxr,y)-get(2,0,maxr,x-1));
}
return 0;
}