传送门:QAQQAQ
题意:给一个长度为$n$的区间,每次可以进行两种操作:
1.在$[l,r]$这个区间里放置一个和之前种类不同的炸弹
2.查询在$[l,r]$区间内有多少种不同种类的炸弹
思路:第一反应是线段树:但一直没想出来线段树来维护什么
和?和可能会因为炸弹覆盖区间长度为改变,而不是因为炸弹种类而改变。
最大值?可能两个完全不相交的区间却有两种不同的炸弹
所以我们要换一个思路想:我们可以把题目转化成这样:给你一个区间,求之前那些区间和该区间相交的有几个
再想:两个区间相交有什么特点呢?两个区间相离又有什么特点呢?——两个区间相离要么是$l1>r2$,要么是$r1
我们就很容易找到思路了:我们建立两个线段树分别维护区间内$l,r$分别出现的次数
然后对于每一个询问的$l,r$,在$[1,l-1]$区间内查询$r$有几个(cnt1),在$[r+1,n]$查询$l$有几个(cnt2),然后用之前的总区间个数减去($cnt1+cnt2$)即可。
(思路很像CF1190D,维护位置存在点的个数,一般这种题都要用离散化(莫名又想到CDQ分治……))
代码:(线段树当然可以,但树状数组似乎更方便)
#include
using namespace std;
const int N=100050;
int sum[2][N*4],n,m;//0:l 1:r
void build(int x,int l,int r)
{
if(l==r)
{
sum[1][x]=0;
sum[0][x]=0;
return;
}
int mid=(l+r)>>1;
build(x+x,l,mid);
build(x+x+1,mid+1,r);
}
void update(int x,int l,int r,int pos,int bl)
{
if(l==r)
{
sum[bl][x]++;
return;
}
int mid=(l+r)>>1;
if(mid>=pos) update(x+x,l,mid,pos,bl);
else update(x+x+1,mid+1,r,pos,bl);
sum[bl][x]=sum[bl][x+x]+sum[bl][x+x+1];
}
int query(int x,int l,int r,int L,int R,int bl)
{
int ret=0;
if(L>R) return 0;
if(L<=l&&r<=R) return sum[bl][x];
int mid=(l+r)>>1;
if(mid>=L) ret+=query(x+x,l,mid,L,R,bl);
if(mid1,mid+1,r,L,R,bl);
return ret;
}
int main()
{
int sum=0;
scanf("%d%d",&n,&m);
while(m--)
{
int x,y,opt;
scanf("%d%d%d",&opt,&x,&y);
if(opt==1)
{
update(1,1,n,x,0);
update(1,1,n,y,1);
sum++;
}
else
{
int now=0;
now+=query(1,1,n,y+1,n,0);
now+=query(1,1,n,1,x-1,1);
printf("%d\n",sum-now);
}
}
return 0;
}
View Code