Description
Bobo 精通数据结构!他想维护一个线段的集合 S。初始时,S 为空。他会依次进行 q 次操作,操作有 2 种。
- 类型 1:给出 l, r,向集合 S 中插入线段 [l, r].
- 类型 2:给出 l, r,询问满足 [x, y]∈S 且 x ≤ l ≤ r ≤ y 的线段 [x, y] 数量。
帮 Bobo 求出每次询问的答案。
- 1 ≤ n, q ≤ 105
- ti ∈ {1, 2}
- 1 ≤ li ≤ ri ≤ n
- 对于 ti = 2 的操作,ri − li ≤ 2 成立。
- 数据组数不超过 10.
Input
输入文件包含多组数据,请处理到文件结束。
每组数据的第一行包含 2 个整数 n 和 q. 其中 n 表示操作中 r 的最大值。
接下来 q 行中的第 i 行包含 3 个整数 ti, li, ri,表示第 i 个操作属于类型 ti,对应的参数是 li 和 ri.
Output
对于每个类型 2 的询问,输出 1 个整数表示对应的数量。
Sample Input
1 2 1 1 1 2 1 1 4 4 1 1 4 2 2 3 1 1 4 2 2 3
Sample Output
1 1 2
这是一道可以用线段树写的题目,注意类型2的操作,类型 2:给出 l, r,询问满足 [x, y]∈S 且 x ≤ l ≤ r ≤ y 的线段 [x, y] 数量。
即给定的l,r一定要有完整的一条线段覆盖在上面才可以。
假设给定4个点,4种操作
操作1: 插入1-4线段。
操作2:插入3-4线段。
操作3:查询区间3-4。
操作4:查询区间2-3。
此时应该输出 2和1(第二次查询中只有一条线段覆盖了2-3)。
建树用两个数组记录开始和结束的点,用cnt记录插入操作的次数,对于每次查询如果l-r如果开始的数组比l+1大就用cnt-区间在l+1到n的线段,然后减去结束点在1到r-1的线段数就是答案了。
具体看代码理解
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=1e5+5;
int ll[maxn*4],lr[maxn*4];
void updata(int l,int r,int n,int x,int t[])
{
if(l==r)
{
t[n]++;
return ;
}
int mid=(l+r)>>1;
if(mid>=x) updata(l,mid,n<<1,x,t);
else updata(mid+1,r,n<<1|1,x,t);
t[n]=t[n<<1]+t[n<<1|1];
}
int getSum(int l,int r,int n,int ll,int rr,int t[])
{
if(l>rr||r<ll) return 0;
if(l>=ll&&r<=rr) return t[n]; //区间内直接加上线段 数然后返回就可以了
int mid=(l+r)>>1;
int ans=0;
if(mid>=ll) ans+=getSum(l,mid,n<<1,ll,rr,t);
if(mid<rr) ans+=getSum(mid+1,r,n<<1|1,ll,rr,t);
return ans;
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
memset(ll,0,sizeof ll);
memset(lr,0,sizeof lr);
int cnt=0;
while(m--)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
if(a==1)
{
cnt++;
updata(1,n,1,b,ll);// ll记录线段开始端点
updata(1,n,1,c,lr);// lr记录线段结束端点
}
else
{
//对于每次查询l-r,用cnt减去 开始端点比l大的线段
// 用cnt减去结束端点比r小的线段就是答案了
printf("%d\n",(cnt-getSum(1,n,1,b+1,n,ll)-getSum(1,n,1,1,c-1,lr)));
}
}
}
}