zoukankan      html  css  js  c++  java
  • 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;
    }

      

  • 相关阅读:
    Android——继续深造——从安装Android Studio 2.0开始(详)
    PHP——安装wampserver丢失MSVCR110.dll
    Marza Gift for GDC 2016
    Retrieve OpenGL Context from Qt 5.5 on OSX
    Space Time Varying Color Palette
    Screen Space Depth Varying Glow based on Heat Diffusion
    Visualization of Detail Point Set by Local Algebraic Sphere Fitting
    Glass Dragon
    Jump Flood Algorithms for Centroidal Voronoi Tessellation
    京都之行
  • 原文地址:https://www.cnblogs.com/lyqlyq/p/7201619.html
Copyright © 2011-2022 走看看