[解题记录] P2184 贪婪大陆
P2184 贪婪大陆
题意简述
- 若 \(q=1\),则表示在 \([l, r]\) 这段区间布上一种地雷;
- 若 \(q=2\),则表示询问当前 \([l, r]\) 区间总共有多少种地雷。
解题思路
如果一般的线段树题一样,去维护题目要我们求的 \(——\) 区间内地雷的种数,就会发现,这个东西不满足区间可加性,例如当我们要合
并两个区间时,如果两个区间上的种类是一样的,那么只能算一种。
接着我们可以发现,这其实是一道区间覆盖的题目,求有多少种地雷其实就等价于求一个 \([l,r]\) 被多少个线段覆盖,转化为线段覆盖题,
就可以只考虑左右端点了(这应该是一种套路),如果维护一个区间里含有的线段的左右端点可行吗?显然是不可行的,因为这样会
会出现算重的情况。
区间里出现了左端点,则表示有一个线段从这里开始,右端点表示有一点端点从这里结束,那么如果求出 \([1,r]\) 里所有的左端点,再求出
\([1,l-1]\) 里所有的右端点在相减,不就能得到我们所求的了?
这其实就是所有可能出现的线段减去可能出现的线段里面的一定不在的线段,就是一定在的线段了
Code
void pushup(int p){
t[p].l=t[ls].l+t[rs].l;
t[p].r=t[ls].r+t[rs].r;
return;
}
void updl(int p,int l,int r,int x){
if(l==r){
t[p].l+=1;
return;
}
int mid=(l+r)>>1;
if(x<=mid) updl(ls,l,mid,x);
else updl(rs,mid+1,r,x);
pushup(p);
}
void updr(int p,int l,int r,int x){
if(l==r){
t[p].r+=1;
return;
}
int mid=(l+r)>>1;
if(x<=mid) updr(ls,l,mid,x);
else updr(rs,mid+1,r,x);
pushup(p);
}
int ql(int p,int l,int r,int x,int y){
int res=0;
if(x<=l&&y>=r){
return t[p].l;
}
int mid=(l+r)>>1;
if(x<=mid) res+=ql(ls,l,mid,x,y);
if(y>mid) res+=ql(rs,mid+1,r,x,y);
return res;
}
int qr(int p,int l,int r,int x,int y){
int res=0;
if(x<=l&&y>=r){
return t[p].r;
}
int mid=(l+r)>>1;
if(x<=mid) res+=qr(ls,l,mid,x,y);
if(y>mid) res+=qr(rs,mid+1,r,x,y);
return res;
}
signed main(){
n=read();m=read();
for(int i=1;i<=m;++i){
int q,l,r;
q=read();l=read();r=read();
if(q==1){updl(1,1,n,l);updr(1,1,n,r);}
else{
int L=ql(1,1,n,1,r);
int R=qr(1,1,n,1,l-1);
printf("%lld\n",L-R);
}
}
return 0;
}