前言
线段树是什么我就不在这里详细讲解了大家在用线段树时,那些简单的题型应该都没有问题了吧?
——那好,我们一起来看一道稍微复杂一点的水题吧!
【高级数据结构】自动售票系统
题目描述
某次列车途径C个城市,城市编号依次为1到C,列车上共有S个座位,铁路局规定售出的车票只能是坐票,即车上所有的旅客都有座。售票系统是由计算机执行的,每一个售票申请包含三个参数,分别用O、D、N表示。O为起始点,D为目的站,N为车票张数。售票系统对该售票申请作出受理或不受理的决定,只有在从O到D的区段内列车上都有N个或N个以上的空座位时该售票申请才被受理。
请你写一个程序,实现这个自动售票系统。
输入
第1行:3个整数C(1<=C<=60000),S(1<=S<=60000),R(1<=R<=60000)。C为城市个数,S为列车上的座位数,R为所有售票申请总数。
接下来R行每行为一个售票申请,用3个整数O,D,N表示,O为起始站,D为目的站,N为车票张数,其中1<=D<=C,1<=O<=C,所有的售票申请按申请的时间从早到晚给出。
输出
共R行,每行输出一个“YES”或“NO”,表示当前的售票申请被受理或不被受理。
样例输入
4 6 4
1 4 2
1 3 2
2 4 3
1 2 3
样例输出
YES
YES
NO
NO
解题
第一步:把它转换成线段树问题
题目中是不是说:“途径C个城市,城市编号依次为1到C”? 好的!我们不妨就拿C个城市连起来的这条直线做个线段树,那么每个区间就存的是这段路上车里最多会有多少人,因为如果这段路上人最多时不足N个位子了,那还买个*票啊。(漏了脏字)
所以可以以此判断XX票受不受理。
第二步:怎么利用线段树
有两步:判断XX票是否受理、处理XX票。
判断很简单,只需查找区间内有没有哪个小区间人数超标,递归遍历一次。关键问题是在搜到了一个大区间内时,怎样可以更节约时间?
比如要查找区间(a,b),递归进入了一个大区间(l,r)。如图:
图中情况下(a,b)是可以直接受理的,因为(l,r)的人数都不足16,那它的子区间也肯定不足16。
但是如果是这种情况:
①
②
①中大区间人数超标,但是左子区间没有,(a,b)应该受理才对,所以这种情况肯定要继续往下搜。
但是在②中继续往下搜就显得很傻屌没有必要,就会浪费时间导致超时。
所以不妨在②这种(l,r)左右子区间人数都相等的情况下,给它做个标记,这样就不会继续往下搜显得很傻屌没有必要了。
处理XX票就是把区间(O,D)加上N。(大写字母打起来好麻烦)
同样地递归遍历,又可以用到这个标记。
再设要处理区间(a,b),递归进入了一个区间(l,r)。
如果a=l、b=r(重合),就直接把(l,r)的人数加上N。
如果(a,b)在(l,r)内,就要继续往下递归了。但如果(l,r)是被标记的(所有区间初始时都是被标记的),那么它的子区间就有可能为空,就要把子区间人数都赋值为与(l,r)相等再继续往下递归。
递归回溯回来后,(因为子区间的值可能改变了)肯定要把(l,r)更新,而且还要判断(l,r)是否重新标记(不然就永远是被标记的,肯定不行)。
这样就可以完美地利用线段树快速解决了(nice)。
最终代码是这样的:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,s,m,t[240005],a,b,c;
bool g[240005]; //标记数组
bool ac(int l,int r,int i,int a,int b){
int mid=(l+r)/2;
if(s-t[i]>=c)return 1;
else
if(g[i]){
bool ar=1;
if(a<mid)ar=ac(l,mid,i*2,a,min(b,mid));
if(b>mid)ar=ar&&ac(mid,r,i*2+1,max(mid,a),b);
return ar;
}
else return 0;
}
void pl(int l,int r,int i,int a,int b){
int mid=(l+r)/2;
if(l==a&&r==b&&!g[i]){
t[i]+=c;
return;
}
if(!g[i])t[i*2]=t[i*2+1]=t[i];
if(a<mid)pl(l,mid,i*2,a,min(mid,b));
if(b>mid)pl(mid,r,i*2+1,max(mid,a),b);
if(t[i*2]==t[i*2+1]&&!g[i*2]&&!g[i*2+1])t[i]=t[i*2],g[i]=0;
else t[i]=max(t[i*2],t[i*2+1]),g[i]=1;
return;
}
int main()
{
scanf("%d%d%d",&n,&s,&m);
while(m--){
scanf("%d%d%d",&a,&b,&c);
if(ac(1,n,1,a,b)){
puts("YES");
pl(1,n,1,a,b);
}
else puts("NO");
}
return 0;
}
尾语
欢迎关注我的博客 偶耶(xiong j x)