1. 程式人生 > >洛谷 題解 P1083 【借教室】

洛谷 題解 P1083 【借教室】

0x00 先看資料範圍

$ 1≤n,m≤10^6 $,第一反應 \(O(nlogn)\)

0x01 5 pts

直接輸出 '0' 即可。

不要問我怎麼知道輸出 '0' 可以拿 5 pts。

保持微笑.jpeg *1

0x02 40~50 pts

考慮暴力。

按照題意列舉即可。

核心虛擬碼如下:

定義 n, m 為 int 型變數 
定義 r 為 int 型陣列,大小為 Max_N 
讀入 n, m
讀入 r 陣列 
定義 d, s, t 為 int 型變數 
使用變數 i 從 1 到 m 遍歷
    讀入 d, s, t
    使用變數 j 在 r 數組裡從 s 到 t 遍歷
        r[j] 減去 d;
        如果 r[j] < 0 那麼 
            輸出 "-1" 和回車 
            輸出 i 
            結束程式
        否則 
            j 指標後移
輸出 "0" 
結束程式 

然後,考察你程式的常數的時候到了。

保持微笑.jpeg *2

0x03 70 pts

一看就是線段樹。

然而,眾所周知,線段樹的常數是比較大的,所以只有 70 pts。

當然,我也看見了用線段樹A題的大佬,在此表示由衷的敬意.

0x04 深入思考

回憶我們預測的時間複雜度:\(O(nlogn)\)

開始猜演算法

——阮行止

保持微笑.jpeg *3

然後想到二分答案

0x05 二分?

設:二分答案的內容為最多可以滿足第 mid 個人的需求

然後開始想 judge 函式。

這時候拼盡腦子想 \(O(n)\) 演算法

然後想到差分

0x06 二分 judge 函式之差分

虛擬碼:

函式引數:x(int 型整數) // 表示可否滿足第 x 個人的需求
    Clear dif // dif 是大小為 Max_N 的 int 型陣列
    For i (i between [1, x])
        dif[s[i]] = dif[s[i]] + d[i]
        dif[t[i] + 1] = dif[t[i] + 1] - d[i]
    now = 0
    For i (i between [1, n])
        now += dif[i];
        If now > r[i] Return false;
    Return true;
} 

0x07 二分

int l = 0, r = m, mid;
while (l < r) {
    mid = (l + r + 1) >> 1;
    if (judge(mid)) l = mid;
    else r = mid - 1;
}
if (l == m) putchar('0');
else puts("-1"), write(l + 1);

幾點說明:

  1. 如果 judge(m) 為真,說明所有訂單均可滿足
  2. 因為 l 表示最多可以滿足第 l 個人的需求,所以第一個需要修改訂單的人的編號為 l + 1

0x08 程式碼

// luogu-judger-enable-o2
/**
 * Problem: P1083 借教室. 
 * Author:  航空信奧. 
 * Date:    2018/08/23. 
 * Upload:  Luogu. 
 */
#include <stdio.h>
#include <string.h>

namespace HangKongXinAo {
    template <typename _TpInt> inline _TpInt read();
    template <typename _TpInt> inline void write(_TpInt x);
    
#   define Max_N 1000007
    
    int n, m, r[Max_N];
    int d[Max_N], s[Max_N], t[Max_N];
    int dif[Max_N]; 
    
    bool judge(int x)
    {
        memset(dif, 0, sizeof(dif));
        for (int i = 1; i <= x; i++) {
            dif[s[i]] += d[i];
            dif[t[i] + 1] -= d[i]; 
        }
        int now = 0;
        for (int i = 1; i <= n; i++) {
            now += dif[i];
            if (now > r[i]) return false;
        }
        return true;
    } 
    
    void Binary_search() 
    {
        int l = 0, r = m, mid;
        while (l < r) {
            mid = (l + r + 1) >> 1;
            if (judge(mid)) l = mid;
            else r = mid - 1;
        }
        if (l == m) putchar('0');
        else puts("-1"), write(l + 1);
    }
    
    int main() 
    {
        n = read<int>();
        m = read<int>();
        for (int i = 1; i <= n; i++) {
            r[i] = read<int>();
        }
        for (int i = 1; i <= m; i++) {
            d[i] = read<int>();
            s[i] = read<int>();
            t[i] = read<int>();
        }
        Binary_search();
        return 0;
    }

    char BufferRead[1 << 17];
    int rLen = 0, rPos = 0;
    inline char Getchar()
    {
        if (rPos == rLen) rPos = 0, rLen = fread(BufferRead, 1, 1 << 17, stdin);
        if (rPos == rLen) return EOF;
        return BufferRead[rPos++];
    } 

    template <typename _TpInt>
    inline _TpInt read()       
    {
        register int flag = 1;
        register char c = Getchar();
        while ((c > '9' || c < '0') && c != '-') 
            c = Getchar();
        if (c == '-') flag = -1, c = Getchar();
        register _TpInt init = (c & 15);
        while ((c = Getchar()) <= '9' && c >= '0') 
            init = (init << 3) + (init << 1) + (c & 15);
        return init * flag;
    }

    template <typename _TpInt>
    inline void write(_TpInt x)
    {
        if (x < 0) {
            putchar('-');
            write<_TpInt>(~x + 1);
        }
        else {
            if (x > 9) write<_TpInt>(x / 10);   
            putchar(x % 10 + '0');
        }
    }
}

int main()
{
    HangKongXinAo::main();
    return 0;
}