在这里先推荐篇博客,关于线段树入门的,自己感觉博主写得挺好的。
https://www.cnblogs.com/TenosDoIt/p/3453089.html
来,给道例题。
Team Rocket
链接:https://www.nowcoder.com/acm/contest/144/I
来源:牛客网
时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
There are n trains running between Kanto and Johto region. Assuming the railway is a number line, the i-th train travels from coordinate li to coordinate ri (both inclusive).
One day, m Team Rocket members invaded the railway system successfully. The i-th Team Rocket member was going to destroy the transportation hub with coordinate xi. Once a transportation hub within the driving range of a train is destroyed, the train's itinerary will be canceled immediately.
Giovanni wants to know how many train trips will be firstly canceled after each attack.
After all the attacks finished, for each train Giovanni needs to know that in which attack its itinerary was firstly canceled, or it was unaffected at all.
输入描述:
The input starts with one line containing exactly one integer T, which is the number of test cases.
For each test case, the first line contains two integers n and m, indicating the number of trains and the number of Team Rocket members.
Each of the next n lines contains 2 integers li and ri, indicating the driving range of the i-th train.
Each of the next m lines contains exactly one integer yi. , where xi is the transportation hub that Team Rocket members would destroy in the i-th attack, resi-1 is the product of the indexes of trips cancelled by the (i-1)-th attack and means exclusive or.
If no such trip exists, resi-1 is considered to be 0.
- 1 ≤ T ≤ 5.
- 1 ≤ n,m ≤ 2 x 105.
- -109 ≤ li ≤ ri ≤ 109.
- -109 ≤ xi ≤ 109.
输出描述:
For each test case, output one line "Case #x:" first, where x is the test case number (starting from 1).
Then output m lines, each line of which contains exactly one integer, indicating the number of train trips firstly canceled after the i-th attack.
Finally output one line, containing n integers, where the i-th integer is the time when the i-th train trip is firstly canceled or 0 if it is not affected.
示例1
输入
复制
1
3 4
1 3
2 6
-999 1000000000
-1000
1
5
2
输出
复制
Case #1:
0
2
1
0
2 3 2
参考博客:https://blog.csdn.net/weixin_41156591/article/details/81481944
题意:T组测试样例,每组先给出N和M表示有N条铁路和M次询问,随后给出N行L和R表示在数轴上当前铁路的位置,再给出M行Y,Y需要先与上一次询问得到的"所有被炸的铁路的下标的乘积%MOD"异或,然后作为新的爆炸点询问,输出要求对每次询问一个爆炸点,给出该爆炸点炸了几条铁路(被炸过的不算入内),最后在输出一行N个数,表示第1~N条铁路是在第几次询问被炸的(若没有被炸则为0).
解题思路:很显然这是一道防离线做法的题目(将每次的询问都异或上一次询问的一些特殊处理得到新的询问).这里我们考虑的做法是线段树维护该铁路系统,首先将所有铁路都编号从1~N,根据他们的L进行升序排序一波,然后作为线段树的叶子节点,最主要的处理是线段树维护的是该节点能到达的最大R值,那么对于每次被炸掉的铁路,我们考虑将其节点的R修改为-INF即可,至于在询问的时候,我们还需要考虑的是当前询问的爆炸点最左能炸到的铁路位置pos,之后二分的时候若是pos<mid+1,或当前爆炸点位置y大于当前树节点的R即不用进入~从而将每次询问的时间复杂度压缩到O(logn),总的时间复杂度为O(nlogn).
这些都是其他博主写的,我就补充一下。
为什么维护的是该节点能到达的最大R值,而不是L值呢?
因为我们已经按L升序排好了,那么在查询时,只要y不大于根节点的值,那y就一定在此范围内(还因为查询时还特判了一下,y>tre[root].r,表示y肯定不在此区间[l,tre[root].r]内)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
#define mod 998244353
#define INF 0x3f3f3f3f
const int maxn=200010;
struct node{
int l,r,index;
bool operator< (const node &a) const{
return l<a.l;
}
}a[maxn],tre[maxn<<2];
///用这个就得这样写upper_bound 调用node{(int)y, 0, 0}
//struct node
//{
// int l, r, index;
// node() {} ///写这个跟在upper_bound 调用node(y, 0, 0)有用;
// node(int L, int R, int id)
// {
// l = L, R = r, index = id;
// }
// bool operator <(const node &a)const
// {
// return l < a.l;
// }
//} a[maxn], tre[maxn << 2]; ///tre,线段树,一般开的范围是最大的4倍
///此种定义结构体对应下面注释第二种
LL y,res;///y表示爆炸点,res表示要跟y异或的
int cnt,pos;
///cnt表示此询问炸了几条铁路,pos表示第一个大于当前y的值在升序后的a中哪个位置
int ans[maxn]; ///存储第i条铁路被哪一个爆炸点给灭了
void build(int root,int L,int R) ///建立线段树
{
if(L>R) return;
if(L==R){
tre[root].r=a[L].r;
tre[root].index=a[L].index;
return;
}
int mid=(L+R)>>1;
build(root*2,L,mid);
build(root*2+1,mid+1,R);
tre[root].r=max(tre[root*2].r,tre[root*2+1].r);
return;
}
void query(int root,int L,int R,int x)
{
if(L>R||y>tre[root].r) return;
///第一个L>R 就是判断根节点被赋值-INF时特判用的
///第二个是表示y不在[l,tre[root].r]范围内
if(L==R)
{
cnt++;
tre[root].r=-INF; ///此铁路已被灭
res=(res*tre[root].index)%mod; ///给下一次查询异或用
ans[tre[root].index]=x;
return;
}
int mid=(L+R)>>1;
query(root*2,L,mid,x);
if(pos>=mid+1)
query(root*2+1,mid+1,R,x);
tre[root].r=max(tre[root*2].r,tre[root*2+1].r);
return;
}
int main()
{
int ncase,n,m,x;
scanf("%d",&ncase);
for(int t=1;t<=ncase;t++)
{
memset(ans,0,sizeof(ans));
printf("Case #%d:\n",t);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].l,&a[i].r),a[i].index=i;
sort(a+1,a+1+n);
build(1,1,n); ///从根节点1开始,建立线段树
res=0; ///题意,前面没有铁路被炸,跟0异或
for(int i=1;i<=m;i++)
{
scanf("%lld",&y);
y^=res;
cnt=0;///初始化
res=1; ///查询时可能会乘,故赋值为1
pos=upper_bound(a+1,a+1+n,node{(int)y,0,0})-a-1;
/// 这种调用也行,跟注释的第二种想对应
/// pos = upper_bound(a + 1, a + 1 + n, node(y, 0, 0)) - a - 1;
if(pos>0) ///要pos==0,表示y小于最小的左边界点,没办法了,此时的y只能滚蛋了
query(1,1,n,i);
if(!cnt) ///这次一条铁路都没炸掉,res赋值为0,下次异或o
res=0;
printf("%d\n",cnt);
}
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
return 0;
}
我的标签:做个有情怀的程序员。