C++线段树运用进阶:【高级数据结构】自动售票系统

前言

线段树是什么我就不在这里详细讲解了大家在用线段树时,那些简单的题型应该都没有问题了吧?

——那好,我们一起来看一道稍微复杂一点的题吧!

【高级数据结构】自动售票系统

题目描述

某次列车途径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)

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/86608530