• 1266. [NOIP2012] 借教室


    ★★☆   输入文件:classrooms.in   输出文件:classrooms.out   简单对比

    时间限制:1 s   内存限制:128 MB

    【题目描述】

    在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 

    面对海量租借教室的信息,我们自然希望编程解决这个问题。 

    我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室。 

    我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提 供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。 

    借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教 室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。 

    现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改 订单。

    【输入格式】

    第一行包含两个正整数n,m,表示天数和订单的数量。 

    第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。 

    接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在 第几天。 

    每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

    【输出格式】

    如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

    【样例输入】

    4 3 
    2 5 4 3 
    2 1 3 
    3 2 4 
    4 2 4 

    【样例输出】

    -1
    2

    【输入输出样例说明】

    第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到 第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。

    【数据范围】

    对于 10%的数据,有1 ≤ n,m ≤ 10; 

    对于 30%的数据,有1 ≤ n,m ≤ 1000; 

    对于 70%的数据,有1 ≤ n,m ≤ 10^5; 

    对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ ri,dj ≤ 10^9,1 ≤ sj ≤ tj ≤ n。

    维护区间最小值:

    //直接线段树区间查询最小值的话是过不了的
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    const int N = 1e6 + 10;
    
    #define lson jd << 1
    #define rson jd << 1 | 1
    #define oo 999999999
    #define LL long long
    
    struct Node{
        LL l, r, minn, f;
    }T[N << 2];
    int n, m;
    LL ans_min;
    
    inline int read(){
        int x = 0; char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x;
    }
    
    void down(int jd){
        T[lson].f += T[jd].f;
        T[rson].f += T[jd].f;
        T[lson].minn += T[jd].f;
        T[rson].minn += T[jd].f;
        T[jd].f = 0;
    }
    
    void build_tree(int l, int r, int jd){
        T[jd].l = l; T[jd].r = r;
        if(l == r){
            T[jd].minn = read();
            return ;
        }
        int mid = (l + r) >> 1;
        build_tree(l, mid, lson);
        build_tree(mid + 1, r, rson);
        T[jd].minn = min(T[lson].minn, T[rson].minn);
    }
    
    void Sec_ask(int l, int r, int jd, int x, int y){
        if(x <= l && r <= y){
            ans_min = min(ans_min, T[jd].minn);
            return ;
        }
        if(T[jd].f) down(jd);
        int mid = (l + r) >> 1;
        if(x <= mid) Sec_ask(l, mid, lson, x, y);
        if(y > mid) Sec_ask(mid + 1, r, rson, x, y);
    }
    
    void Sec_g(int l, int r, int jd, int x, int y, int yj){
        if(x <= l && r <= y){
            T[jd].minn += yj;
            T[jd].f += yj;
            return ;
        }
        int mid = (l + r) >> 1;
        if(x <= mid) Sec_g(l, mid, lson, x, y,yj);
        if(y > mid) Sec_g(mid + 1, r, rson, x, y, yj);
        T[jd].minn = min(T[lson].minn, T[rson].minn);
    }
    
    int main()
    {
        n = read();
        m = read();
        build_tree(1, n, 1);
        for(int i = 1; i <= m; i ++)
        {
            int want = read(), l = read(), r = read();
            ans_min = oo;
            Sec_ask(1, n, 1, l, r);
            if(ans_min < want){
                printf("-1
    %d", i);
                return 0;
            }
            Sec_g(1, n, 1, l, r, -want);
        }
        
        return 0;
    }

    前缀和+二分answer

    /*
    用某一天的前缀和表示该天需要的教室数 比如一开始数列a是0 0 0 0 0 0
     前缀和0 0 0 0 0 0  3到5天需要2的教室 将a[3]+=2,a[6]-=2 数列变为0 
     0 2 0 0 -2 前缀和变为0 0 2 2 2 0  这样就实现了增加3-5需要的教室数  
     二分订单数量,判断一下前mid个订单是否可以。具体操作是前缀和处理,
     即每读入一个订单就在起始天+要借的房间数量,在结束天的下一天减去要
     借的房间数量。然后比较每一天的前缀和与本天总共的房间数的大
     小,如果房间数<前缀和,就说明前mid个订单有问题,向前二分;否则就向
     后二分。证明:在一个订单的起始天+要借的房间数量,在结束天的
     下一天减去要借的房间数量。设一个数组c[i],记录前缀和。读入的数据是
     d,s,t C[s]:=c[s]+d;c[t+1]:=c[t+1]-d;  那么如果第i天在s和t之间,那么
     前i天的sum{c[i]}中有c[s],相当于已经记下第i天的订单数量了。如果第i天
     在t之后,前i天的sum{c[i]}中有c[s]和c[t],因为c[s]+d+c[t+1]-d=c[s]+c[t],
     所以这个订单只对s和t中间天数起作用。得证! 
    */
    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    const int N = 1000010;
    
    int n, m, ans;
    int a[N], d[N], x[N], y[N], s[N], sum;
    
    inline int read(){
        int x = 0; char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x;
    }
    
    inline bool judge(int v){
        memset(s, 0, sizeof(s));
        sum = 0;
        for(int i = 1; i <= v; i ++)
        {
            s[x[i]] += d[i];
            s[y[i]+1] -= d[i];
        }
        for(int i = 1; i <= n; i ++)
        {
            sum += s[i];
            if(sum > a[i]) return 0;
        }
        return 1;
    }
    
    int main()
    {
        n = read();
        m = read();
        for(int i=1; i<=n; i++) a[i] = read();
        for(int i=1; i<=m; i++) d[i] = read(), x[i] = read(), y[i] = read();
        int l = 1, r = m;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(!judge(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        if(!ans) printf("0");
        else printf("-1
    %d",ans);
        return 0;
    }

      

  • 相关阅读:
    手工去除 dll 和 exe 文件的数字签名
    针式PKM中级应用:文件的5S(归档整理删除)
    针式PKM初级应用:如何避免收集重复的资料?
    了解更多:什么是个人知识管理?
    如何选用知识管理软件?
    与阿朱聊个人知识管理:体系和方法论层面
    针式PKM初级应用:针式PKM每天应使用多少小时?
    战略人生
    针式PKM初级应用:针式PKM更适合管理什么样的文件
    Data, Information, and Knowledge Management Software "What software should I use?"
  • 原文地址:https://www.cnblogs.com/lyqlyq/p/7201619.html
Copyright © 2020-2023  润新知