From 61863d09a4a118c8768bc630511f8528e28b5ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Sun, 17 Dec 2023 09:53:33 +0800 Subject: [PATCH] 'commit' --- TangDou/Topic/PrefixAndSuffix/P1083.cpp | 43 ++++++++++++++++ TangDou/Topic/PrefixAndSuffix/P1083_0.cpp | 28 +++++++++++ .../前缀和和差分洛谷题单总结.md | 49 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 TangDou/Topic/PrefixAndSuffix/P1083.cpp create mode 100644 TangDou/Topic/PrefixAndSuffix/P1083_0.cpp diff --git a/TangDou/Topic/PrefixAndSuffix/P1083.cpp b/TangDou/Topic/PrefixAndSuffix/P1083.cpp new file mode 100644 index 0000000..e260395 --- /dev/null +++ b/TangDou/Topic/PrefixAndSuffix/P1083.cpp @@ -0,0 +1,43 @@ +#include +using namespace std; +const int N = 1000010; +#define int long long +#define endl "\n" +int n, m; // 天数和订单的数量 +int r[N]; // 第i天学校有r[i]个教室可借用 +int d[N], s[N], t[N]; // 借的教室数目、从第s天借到t天 +int cf[N]; // 差分数组 +bool judge(int x) { // 判断能不能通过x个人 + memset(cf, 0, sizeof(cf)); // 每次判断都要先初始化差分数组 + int sum = 0; // 记录需要借的教室数 + for (int i = 1; i <= x; i++) { + cf[s[i]] += d[i]; // 因为只会对在s~l之间要借用教室的人产生影响,所以可以差分 + cf[t[i] + 1] -= d[i]; // 差分//注意是t[i]+1,因为要包含t[i]这个点 + } + for (int i = 1; i <= n; i++) { + sum += cf[i]; // 因为cf是差分数组,所以sum就是在第i天的借教室的总数 + if (sum > r[i]) // 如果要借的教室多于空的教室 + return false; // 不可行 + } + return true; // 可行 +} +signed main() { + cin >> n >> m; + for (int i = 1; i <= n; i++) cin >> r[i]; + for (int i = 1; i <= m; i++) cin >> d[i] >> s[i] >> t[i]; + if (judge(m)) { // 如果全部满足 + cout << '0'; // 输出0 + return 0; // 直接结束程序 + } + int l = 1; + int r = m; // 二分左右区间 + while (l < r) { + int mid = (l + r) / 2; + if (judge(mid) == true) // 如果可行 + l = mid + 1; // 增多满足人数 + else // 否则 + r = mid; // 减少满足人数 + } + cout << "-1" << endl + << l; // 输出-1和需要修改的人 +} \ No newline at end of file diff --git a/TangDou/Topic/PrefixAndSuffix/P1083_0.cpp b/TangDou/Topic/PrefixAndSuffix/P1083_0.cpp new file mode 100644 index 0000000..f65eebf --- /dev/null +++ b/TangDou/Topic/PrefixAndSuffix/P1083_0.cpp @@ -0,0 +1,28 @@ +#include +using namespace std; +int n, m; +const int N = 1000010; +int r[N]; +int main() { + cin >> n >> m; + // 每一天可租借教室数 + for (int i = 1; i <= n; i++) cin >> r[i]; + + // 从哪天到哪天,借多少个 + for (int i = 1; i <= m; i++) { + int d, s, t; + cin >> d >> s >> t; + // 从开始天到结束天 + for (int j = s; j <= t; j++) { + r[j] -= d; // 减去借走的教室数 + if (r[j] < 0) { // 小于0了! + cout << -1 << endl + << i << endl; + return 0; + } + } + } + + cout << 0 << endl; + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/前缀和和差分洛谷题单总结.md b/TangDou/Topic/前缀和和差分洛谷题单总结.md index 85da93f..b5165bd 100644 --- a/TangDou/Topic/前缀和和差分洛谷题单总结.md +++ b/TangDou/Topic/前缀和和差分洛谷题单总结.md @@ -85,6 +85,55 @@ int main() { return 0; } ``` +### [$P1083$ [$NOIP2012$ 提高组] 借教室](https://www.luogu.com.cn/problem/P1083) +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170927931.png) + +#### 暴力 +还是先看暴力怎么做吧,对于$m$次借教室,我们可以每次把区间$s\sim t$的空教室数$r-=d$,当有一次$r<0$时,则当前这个人无法被满足,直接输出$-1$和当前这个人的号数,然后直接结束程序。如果$m$次借教室都操作完成后依然没有房间数$r<0$,则说明所有人都可以被满足,则输出$0$。 + +综合上述做法,得分$60$。 + +```cpp {.line-numbers} +#include +using namespace std; +int n, m; +const int N = 1000010; +int r[N]; +int main() { + cin >> n >> m; + // 每一天可租借教室数 + for (int i = 1; i <= n; i++) cin >> r[i]; + + // 从哪天到哪天,借多少个 + for (int i = 1; i <= m; i++) { + int d, s, t; + cin >> d >> s >> t; + // 从开始天到结束天 + for (int j = s; j <= t; j++) { + r[j] -= d; // 减去借走的教室数 + if (r[j] < 0) { // 小于0了! + cout << -1 << endl + << i << endl; + return 0; + } + } + } + + cout << 0 << endl; + return 0; +} +``` +显然,这样做法的时间复杂度时$O(N*M)$的,无法通过此题,从而我们可以推知该题正确的时间复杂度应该是$log$级的。 + +#### 正解 +既然时间复杂度时$log$级的,于是想到了二分。 + +再看到每个人借教室的时间可以看成一个区间,且该区间只会对其他在该区间要借教室的人产生影响,对于区间之外的借教室的人是不会产生影响的,于是又想到了差分。 + +差分序列:(可用于区间增减)记录相邻两个量的变化量,所以当在一段区间$[l,r]$上增加$a$时,只需要在$l$处加$a$,在$r+1$处$-a$即可。 + +对于为什么可以二分:如果一个人无法被满足,则他后面的人全都不能被满足;如果一个人可以被满足,则他前面的人都可以被满足,这恰恰吻合了我们二分的性质。 + ### [$P3406$ 海底高铁](https://www.luogu.com.cn/problem/P3406) ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312170857701.png)