19.6 แบบฝึกหัด
เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น
ข้อ 1 ส่งน้ำแข็งถึงกี่บ้าน
ร้านน้ำแข็งส่งของให้ลูกค้าที่อยู่ริมถนนเส้นตรงเส้นเดียว บ้านลูกค้า N หลังอยู่ที่ตำแหน่ง x1, x2, ..., xN กิโลเมตรจากหลักกิโลเมตรศูนย์ ตำแหน่งติดลบคืออยู่อีกฝั่งของหลัก บ้านหลายหลังอาจอยู่ตำแหน่งเดียวกันได้ เช่น ห้องในตึกเดียวกัน
ร้านวางแผนส่งของ Q วัน วันหนึ่งรถจะจอดที่ตำแหน่ง p และน้ำแข็งละลายเร็ว จึงส่งได้เฉพาะบ้านที่อยู่ห่างจากรถไม่เกิน d กิโลเมตร ไม่ว่าทางซ้ายหรือทางขวา ให้หาว่าแต่ละวันส่งของถึงบ้านได้กี่หลัง
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 1 ≤ Q ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือตำแหน่งของบ้าน โดยที่ -109 ≤ xi ≤ 109
อีก Q บรรทัด แต่ละบรรทัดมีจำนวนเต็ม p และ d คั่นด้วยช่องว่าง โดยที่ -109 ≤ p ≤ 109 และ 0 ≤ d ≤ 109
ข้อมูลออก
Q บรรทัด ตอบตามลำดับวัน บรรทัดละหนึ่งวัน เป็นจำนวนบ้านที่อยู่ห่างจาก p ไม่เกิน d
6 4
3 -2 10 3 7 15
5 2
0 3
12 1
3 03
3
0
2ดูคำใบ้
เรียงตำแหน่งบ้านครั้งเดียว แล้วแต่ละวันนับบ้านที่ตำแหน่งอยู่ตั้งแต่ p - d ถึง p + d ด้วย upper_bound(p + d) ลบ lower_bound(p - d) ตามหน้า 19.2 ระวังบ้านที่อยู่ตรงขอบช่วงพอดี และบ้านหลายหลังที่อยู่ตำแหน่งเดียวกัน วิธีไล่ดูทุกบ้านทุกวันต้องเทียบถึง 4 × 1010 ครั้ง ไม่ทันเวลา
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<long long> houses(n);
for (int i = 0; i < n; i++) {
cin >> houses[i];
}
sort(houses.begin(), houses.end());
for (int k = 0; k < q; k++) {
long long p, d;
cin >> p >> d;
int from = lower_bound(houses.begin(), houses.end(), p - d) - houses.begin();
int to = upper_bound(houses.begin(), houses.end(), p + d) - houses.begin();
cout << to - from << '\n';
}
return 0;
}ข้อ 2 อ่านครบเมื่อไร
น้องจดไว้ว่าตลอด N วันที่ผ่านมา วันที่ i อ่านหนังสือไป ai หน้า บางวันไม่ได้อ่านเลย เพื่อนๆ ในชมรมอยากเทียบกันว่า ถ้าเริ่มนับตั้งแต่วันที่ s จะต้องถึงวันที่เท่าไรน้องจึงอ่านรวมได้อย่างน้อย t หน้า มีคำถามแบบนี้ Q ข้อ
คำตอบของคำถามหนึ่งข้อคือวันที่ d ที่น้อยที่สุด ที่ d ≥ s และ as + as+1 + ... + ad ≥ t ถ้าอ่านถึงวันที่ N แล้วยังไม่ถึง t หน้า ให้ตอบ -1
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 1 ≤ Q ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ a1 ถึง aN โดยที่ 0 ≤ ai ≤ 10000
อีก Q บรรทัด แต่ละบรรทัดมีจำนวนเต็ม s และ t คั่นด้วยช่องว่าง โดยที่ 1 ≤ s ≤ N และ 1 ≤ t ≤ 109
ข้อมูลออก
Q บรรทัด ตอบตามลำดับคำถาม บรรทัดละหนึ่งข้อ เป็นวันที่ d หรือ -1
7 5
30 45 0 0 60 25 40
1 75
3 1
3 60
6 66
2 2002
5
5
-1
-1ดูคำใบ้
สร้าง prefix ของจำนวนหน้าก่อน ถ้าเริ่มวันที่ s จำนวนหน้าที่อ่านถึงวันที่ d คือ prefix[d] - prefix[s - 1] ต้องการให้ค่านี้อย่างน้อย t ก็คือ prefix[d] >= prefix[s - 1] + t ตัวเลขทางขวาเป็นค่าคงที่ของคำถามนั้น จำนวนหน้าไม่ติดลบ prefix จึงเรียงอยู่แล้ว ใช้ lower_bound ค้นตั้งแต่ prefix.begin() + s ตามหน้า 19.2 วันที่ไม่ได้อ่านทำให้มีค่าซ้ำ ต้องได้วันแรก
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<long long> prefix(n + 1, 0);
for (int day = 1; day <= n; day++) {
int pages;
cin >> pages;
prefix[day] = prefix[day - 1] + pages;
}
for (int k = 0; k < q; k++) {
int start;
long long target;
cin >> start >> target;
long long need = prefix[start - 1] + target;
int day = lower_bound(prefix.begin() + start, prefix.end(), need) - prefix.begin();
if (day > n) {
cout << -1 << '\n';
} else {
cout << day << '\n';
}
}
return 0;
}ข้อ 3 ปูกระเบื้องจัตุรัส
ช่างปูกระเบื้องมีกระเบื้องแผ่นสี่เหลี่ยมจัตุรัสขนาดเท่ากัน n แผ่น ลูกค้าอยากได้ลานที่ปูเป็นรูปสี่เหลี่ยมจัตุรัสเต็มผืน ขนาด k × k แผ่น ให้หาว่าลานใหญ่ที่สุดที่ปูได้มีด้านยาวกี่แผ่น กระเบื้องที่เหลือเก็บไว้ใช้งานอื่น ช่างมีงานแบบนี้ T งาน ให้ตอบทุกงาน
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม T โดยที่ 1 ≤ T ≤ 100000
อีก T บรรทัด แต่ละบรรทัดมีจำนวนเต็ม n หนึ่งตัว โดยที่ 0 ≤ n ≤ 1018
ข้อมูลออก
T บรรทัด ตอบตามลำดับงาน บรรทัดละหนึ่งงาน เป็นจำนวนเต็ม k ที่มากที่สุดที่ k × k ≤ n
5
10
16
0
99
10000000000000000003
4
0
9
1000000000ดูคำใบ้
คำตอบคือ k ที่มากที่สุดที่ k × k ≤ n ถ้า k ใช้ได้ k - 1 ก็ใช้ได้ จึงเป็น binary search หา "ใช่" ตัวสุดท้ายแบบโจทย์ตัดไม้ในหน้า 19.3 ที่ต้องปัดจุดกลางขึ้น คำตอบไม่เกิน 109 จึงให้ hi เริ่มที่ 109 ถ้าเริ่มที่ n จุดกลางจะใหญ่จน mid * mid ล้นแม้เป็น long long และอย่าใช้ sqrt ของ double ซึ่งแม่นแค่ราว 16 หลัก ลองกับ n = 999999999999999999 ดู คำตอบต้องเป็น 999999999
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
for (int job = 0; job < t; job++) {
long long n;
cin >> n;
long long lo = 0, hi = 1000000000;
while (lo < hi) {
long long mid = lo + (hi - lo + 1) / 2;
if (mid * mid <= n) {
lo = mid;
} else {
hi = mid - 1;
}
}
cout << lo << '\n';
}
return 0;
}ข้อ 4 ทาสีรั้วโรงเรียน
รั้วไม้ของโรงเรียนมีไม้เรียงกัน N แผ่น หมายเลข 1 ถึง N วันอาสามีนักเรียนมาช่วยทาสีกันแดด Q กลุ่ม กลุ่มที่ j ทาไม้ตั้งแต่แผ่นที่ l ถึงแผ่นที่ r ทับลงไปอีก c ชั้น สีกันแดดจะได้ผลเมื่อหนาอย่างน้อย K ชั้น หลังทุกกลุ่มทาเสร็จ ให้นับว่ามีไม้กี่แผ่นที่สียังหนาไม่ถึง K ชั้น ซึ่งต้องนัดมาทาซ่อมอีกวัน
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N Q และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000, 1 ≤ Q ≤ 200000 และ 1 ≤ K ≤ 1015
อีก Q บรรทัด แต่ละบรรทัดมีจำนวนเต็ม l r และ c คั่นด้วยช่องว่าง โดยที่ 1 ≤ l ≤ r ≤ N และ 1 ≤ c ≤ 109
ข้อมูลออก
จำนวนแผ่นที่สีหนาน้อยกว่า K ชั้น
8 4 3
1 4 1
3 8 2
5 5 1
2 3 15ดูคำใบ้
ถ้าวนทาทีละแผ่นทุกกลุ่ม เมื่อ N กับ Q เป็นสองแสนและทุกกลุ่มทาเกือบทั้งรั้ว ต้องบวกถึง 4 × 1010 ครั้ง ใช้ difference array ในหน้า 19.5 แต่ละกลุ่มจดแค่สองช่อง แล้วสะสมครั้งเดียวก็ได้จำนวนชั้นของทุกแผ่น จำนวนชั้นรวมได้ถึง 2 × 1014 และ K ก็เกิน int ได้ ใช้ long long ทั้งคู่ ระวังกลุ่มที่ทาถึงแผ่นที่ N ซึ่งต้องลบที่ช่อง N + 1
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
long long k;
cin >> n >> q >> k;
vector<long long> diff(n + 2, 0);
for (int j = 0; j < q; j++) {
int l, r, layers;
cin >> l >> r >> layers;
diff[l] += layers;
diff[r + 1] -= layers;
}
long long current = 0;
int thin = 0;
for (int board = 1; board <= n; board++) {
current += diff[board];
if (current < k) {
thin++;
}
}
cout << thin << '\n';
return 0;
}ข้อ 5 ริบบิ้นผูกช่อดอกไม้
ร้านดอกไม้มีริบบิ้นอยู่ N ม้วน ม้วนที่ i ยาว Li เซนติเมตร งานรับปริญญาสัปดาห์หน้ามีช่อดอกไม้ K ช่อ ทุกช่อต้องผูกริบบิ้นหนึ่งเส้นที่ยาวเท่ากันทุกช่อ และยาวเป็นจำนวนเต็มเซนติเมตร ริบบิ้นแต่ละเส้นต้องตัดมาจากม้วนเดียว ห้ามเอาเศษสองม้วนมาต่อกัน เศษที่เหลือจากการตัดทิ้งได้
ริบบิ้นยิ่งยาวช่อยิ่งสวย ให้หาว่าตัดให้ได้อย่างน้อย K เส้นโดยแต่ละเส้นยาวที่สุดได้กี่เซนติเมตร ถ้าตัดเส้นละ 1 เซนติเมตรแล้วยังไม่ถึง K เส้น ให้ตอบ 0
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100000 และ 1 ≤ K ≤ 109
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ L1 ถึง LN โดยที่ 1 ≤ Li ≤ 109
ข้อมูลออก
ความยาวที่มากที่สุดเป็นเซนติเมตร หรือ 0
5 13
730 410 980 260 5551963 20
5 9 40ดูคำใบ้
ถ้าตัดยาว x ซม. ได้จำนวนเส้นรวมเท่ากับผลรวมของ Li / x แบบปัดเศษทิ้ง ยิ่งยาวยิ่งได้น้อย ถ้า x ใช้ได้ x - 1 ก็ใช้ได้ คำตอบคือ "ใช่" ตัวสุดท้ายแบบโจทย์ตัดไม้ในหน้า 19.3 ให้ lo เริ่มที่ 0 hi เริ่มที่ม้วนที่ยาวที่สุด และปัดจุดกลางขึ้น ซึ่งทำให้ไม่ต้องหารด้วย 0 ด้วย ตอนตัดยาว 1 ซม. จำนวนเส้นรวมถึง 1014 ต้องนับด้วย long long ถ้าลองทุกความยาวจากยาวไปสั้น เมื่อคำตอบสั้นแต่ริบบิ้นยาวพันล้าน ต้องลองเกือบพันล้านค่า
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
long long pieces(const vector<int> &lengths, int cut)
{
long long total = 0;
for (int i = 0; i < (int)lengths.size(); i++) {
total += lengths[i] / cut;
}
return total;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long k;
cin >> n >> k;
vector<int> lengths(n);
int lo = 0, hi = 0;
for (int i = 0; i < n; i++) {
cin >> lengths[i];
hi = max(hi, lengths[i]);
}
while (lo < hi) {
int mid = lo + (hi - lo + 1) / 2;
if (pieces(lengths, mid) >= k) {
lo = mid;
} else {
hi = mid - 1;
}
}
cout << lo << '\n';
return 0;
}ข้อ 6 เตาอบขนม
โรงงานขนมมีเตาอบ N เตา ทุกเตาเริ่มอบพร้อมกันที่นาทีที่ 0 และอบต่อเนื่องไม่หยุด เตาที่ i อบขนมเสร็จหนึ่งถาดทุก ti นาที คือเสร็จถาดแรกที่นาทีที่ ti ถาดที่สองที่นาทีที่ 2ti ไปเรื่อยๆ วันนี้มีออร์เดอร์ M ถาด ให้หาว่าต้องใช้เวลาน้อยที่สุดกี่นาทีจึงได้ขนมครบ M ถาด
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ M คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100000 และ 1 ≤ M ≤ 109
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ t1 ถึง tN โดยที่ 1 ≤ ti ≤ 109
ข้อมูลออก
จำนวนนาทีที่น้อยที่สุด
3 10
4 6 1020ดูคำใบ้
ภายใน T นาที เตาที่ i อบได้ T / ti ถาดแบบปัดเศษทิ้ง ถ้า T นาทีพอ T + 1 นาทีก็พอ จึงหา T แรกที่พอด้วยลูปแบบหน้า 19.3 คำตอบไม่เกิน M คูณเวลาของเตาที่เร็วที่สุด ซึ่งถึง 1018 ต้องใช้ long long ทั้ง lo hi mid และตัวนับถาด ถ้าตั้ง hi ใหญ่กว่านั้น ตัวนับถาดอาจล้นแม้เป็น long long ให้หยุดนับทันทีที่ได้ครบ M ถาด
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
bool enough(const vector<long long> &minutes, long long time, long long trays)
{
long long baked = 0;
for (int i = 0; i < (int)minutes.size(); i++) {
baked += time / minutes[i];
if (baked >= trays) {
return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long trays;
cin >> n >> trays;
vector<long long> minutes(n);
long long fastest = LLONG_MAX;
for (int i = 0; i < n; i++) {
cin >> minutes[i];
fastest = min(fastest, minutes[i]);
}
long long lo = 1, hi = fastest * trays;
while (lo < hi) {
long long mid = lo + (hi - lo) / 2;
if (enough(minutes, mid, trays)) {
hi = mid;
} else {
lo = mid + 1;
}
}
cout << lo << '\n';
return 0;
}ข้อ 7 เสาสัญญาณริมถนน
ถนนชนบทเส้นหนึ่งเป็นเส้นตรง มีจุดที่ได้รับอนุญาตให้ตั้งเสาสัญญาณ N จุด จุดที่ i อยู่ห่างจากต้นถนน xi เมตร ไม่มีสองจุดไหนอยู่ตำแหน่งเดียวกัน บริษัทต้องตั้งเสา C ต้นบนจุดเหล่านี้ จุดละไม่เกินหนึ่งต้น เสาที่อยู่ใกล้กันจะรบกวนสัญญาณกัน บริษัทจึงอยากให้ระยะห่างของเสาคู่ที่อยู่ใกล้กันที่สุดมากที่สุดเท่าที่ทำได้ ให้หาระยะนั้น
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ C คั่นด้วยช่องว่าง โดยที่ 2 ≤ C ≤ N ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ x1 ถึง xN ไม่ได้เรียงมา และไม่ซ้ำกัน โดยที่ 0 ≤ xi ≤ 109
ข้อมูลออก
ระยะห่างของเสาคู่ที่ใกล้กันที่สุด ที่มากที่สุดเท่าที่ทำได้
6 3
12 3 30 1 20 811ดูคำใบ้
ทำตามหัวข้อระยะห่างมากที่สุดในหน้า 19.3 เรียงตำแหน่งก่อน แล้วถามว่า "ตั้ง C ต้นให้ทุกคู่ห่างกันอย่างน้อย D ได้ไหม" ตอบด้วย greedy ตั้งต้นแรกที่จุดซ้ายสุด ไล่ไปทางขวา ตั้งต้นถัดไปที่จุดแรกที่ห่างจากต้นล่าสุดอย่างน้อย D ถ้า D ทำได้ D - 1 ก็ทำได้ จึงหา "ใช่" ตัวสุดท้าย ปัดจุดกลางขึ้น ถ้าลองทุกคู่ของจุดเป็นค่า D ที่เป็นไปได้ จะมีถึง 5 × 109 ค่าเมื่อ N เป็นแสน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
bool can_place(const vector<int> &spots, int towers, int gap)
{
int placed = 1;
int last = spots[0];
for (int i = 1; i < (int)spots.size(); i++) {
if (spots[i] - last >= gap) {
placed++;
last = spots[i];
if (placed >= towers) {
return true;
}
}
}
return placed >= towers;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, towers;
cin >> n >> towers;
vector<int> spots(n);
for (int i = 0; i < n; i++) {
cin >> spots[i];
}
sort(spots.begin(), spots.end());
int lo = 0, hi = spots[n - 1] - spots[0];
while (lo < hi) {
int mid = lo + (hi - lo + 1) / 2;
if (can_place(spots, towers, mid)) {
lo = mid;
} else {
hi = mid - 1;
}
}
cout << lo << '\n';
return 0;
}ข้อ 8 ยอดผู้ติดตามเปลี่ยนเท่ากับ K
น้องทำช่องวิดีโอ และจดว่ายอดผู้ติดตามเปลี่ยนไปเท่าไรในแต่ละวันตลอด N วัน วันที่ i เปลี่ยนไป ai คน ถ้ามีคนเลิกติดตามมากกว่าคนติดตามใหม่ ai จะติดลบ น้องอยากรู้ว่ามีกี่ช่วงวันติดกันที่ยอดผู้ติดตามรวมแล้วเปลี่ยนไป K คนพอดี
ช่วงวันคือคู่ l กับ r ที่ 1 ≤ l ≤ r ≤ N และยอดที่เปลี่ยนรวมคือ al + al+1 + ... + ar ช่วงที่ l หรือ r ต่างกันนับเป็นคนละช่วง แม้ผลรวมจะมาจากวันชุดที่ดูคล้ายกัน
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ K คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ -1015 ≤ K ≤ 1015
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ a1 ถึง aN โดยที่ -109 ≤ ai ≤ 109
ข้อมูลออก
จำนวนช่วงวันที่ยอดเปลี่ยนรวมเท่ากับ K พอดี
7 4
2 2 -3 5 0 -1 45ดูคำใบ้
ช่วงวันที่ l ถึง r เปลี่ยนรวม K พอดีเมื่อ prefix[l - 1] = prefix[r] - K ตามหน้า 19.5 ค่าในแต่ละวันกว้างถึง ±109 ใช้อาเรย์นับไม่ได้ ให้เรียงคู่ (prefix, index) แล้วใช้ lower_bound สองครั้งต่อหนึ่งวัน r อย่าลืมใส่ prefix[0] และจำนวนช่วงถึง N(N + 1)/2 ซึ่งเกิน int ลองทุกคู่ l กับ r ต้องทำราว 2 × 1010 คู่เมื่อ N เป็นสองแสน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
long long k;
cin >> n >> k;
vector<long long> prefix(n + 1, 0);
for (int i = 1; i <= n; i++) {
long long change;
cin >> change;
prefix[i] = prefix[i - 1] + change;
}
vector<pair<long long, int>> sorted_prefix(n + 1);
for (int j = 0; j <= n; j++) {
sorted_prefix[j] = make_pair(prefix[j], j);
}
sort(sorted_prefix.begin(), sorted_prefix.end());
long long count = 0;
for (int r = 1; r <= n; r++) {
long long want = prefix[r] - k;
auto from = lower_bound(sorted_prefix.begin(), sorted_prefix.end(), make_pair(want, 0));
auto to = lower_bound(sorted_prefix.begin(), sorted_prefix.end(), make_pair(want, r));
count += to - from;
}
cout << count << '\n';
return 0;
}ข้อ 9 จุดนัดพบ
เพื่อน N คนอยู่บนถนนเส้นตรงเส้นเดียวกัน คนที่ i อยู่ห่างจากต้นถนน xi เมตร และเคลื่อนที่ได้เร็วที่สุด vi เมตรต่อวินาที บางคนเดิน บางคนปั่นจักรยาน ทุกคนออกเดินทางพร้อมกัน เพื่อไปพบกันที่จุดนัดจุดเดียวบนถนน จุดนัดจะอยู่ตรงไหนก็ได้ ไม่จำเป็นต้องเป็นจำนวนเต็ม คนที่ไปถึงก่อนรอได้ ให้หาว่าทุกคนมาพร้อมหน้ากันได้เร็วที่สุดภายในกี่วินาที
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N โดยที่ 1 ≤ N ≤ 100000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ x1 ถึง xN โดยที่ 0 ≤ xi ≤ 109 หลายคนอาจอยู่ที่เดียวกันได้
บรรทัดที่สามมีจำนวนเต็ม N ตัว คือ v1 ถึง vN โดยที่ 1 ≤ vi ≤ 1000
ข้อมูลออก
เวลาที่น้อยที่สุดเป็นวินาที พิมพ์ทศนิยม 4 ตำแหน่ง ชุดทดสอบเลือกไว้แล้วว่าคำตอบจริงไม่อยู่ใกล้กึ่งกลางระหว่างสองค่าที่ปัดได้ คำตอบที่คลาดจากค่าจริงไม่เกิน 10-6 จะปัดได้ตรงกับเฉลยเสมอ
3
0 10 40
2 3 113.33332
5 5
7 90.0000ดูคำใบ้
ถามว่า "ภายใน T วินาที ทุกคนไปเจอกันได้ไหม" ภายใน T วินาที คนที่ i ไปได้ทุกจุดตั้งแต่ xi - viT ถึง xi + viT ทุกคนเจอกันได้เมื่อช่วงทั้งหมดมีจุดร่วม คือขอบซ้ายที่มากที่สุดไม่เกินขอบขวาที่น้อยที่สุด ถ้า T ทำได้ เวลาที่มากกว่าก็ทำได้ ใช้ binary search บนจำนวนจริงแบบหน้า 19.4 วน 100 รอบ ช่วงเริ่มที่ 0 ถึง 109 พิมพ์ด้วย fixed กับ setprecision(4) ถ้าลองทุกคู่ของคน จะมีห้าพันล้านคู่เมื่อ N เป็นแสน
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
bool can_meet(const vector<int> &place, const vector<int> &speed, double time)
{
double left = -1e30, right = 1e30;
for (int i = 0; i < (int)place.size(); i++) {
left = max(left, place[i] - speed[i] * time);
right = min(right, place[i] + speed[i] * time);
}
return left <= right;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> place(n), speed(n);
for (int i = 0; i < n; i++) {
cin >> place[i];
}
for (int i = 0; i < n; i++) {
cin >> speed[i];
}
double lo = 0, hi = 1e9;
for (int step = 0; step < 100; step++) {
double mid = (lo + hi) / 2;
if (can_meet(place, speed, mid)) {
hi = mid;
} else {
lo = mid;
}
}
cout << fixed << setprecision(4) << hi << '\n';
return 0;
}ข้อ 10 แผนรดน้ำต้นไม้
สวนผักของโรงเรียนมีต้นไม้ปลูกเรียงเป็นแถว N ต้น หมายเลข 1 ถึง N ช่วงปิดเทอม ต้นที่ i ต้องได้น้ำรวมอย่างน้อย needi ลิตรจึงจะรอด ครูวางแผนรดน้ำไว้ Q ครั้งตามลำดับ ครั้งที่ j รดต้นตั้งแต่ต้นที่ l ถึงต้นที่ r ต้นละ w ลิตร ครูอยากรู้ว่าต้องรดตามแผนไปกี่ครั้งแรก ทุกต้นจึงได้น้ำครบตามที่ต้องการ จะได้หยุดรดหลังจากนั้นเพื่อประหยัดน้ำ
คำตอบคือ k ที่น้อยที่สุดที่หลังจากรดครั้งที่ 1 ถึง k แล้ว ทุกต้นได้น้ำรวมอย่างน้อย needi ลิตร ถ้าทุกต้นต้องการ 0 ลิตรตั้งแต่แรก คำตอบคือ 0 ถ้ารดครบทั้ง Q ครั้งแล้วยังมีต้นที่ได้น้ำไม่พอ ให้ตอบ -1
ข้อมูลเข้า
บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 200000 และ 1 ≤ Q ≤ 200000
บรรทัดที่สองมีจำนวนเต็ม N ตัว คือ need1 ถึง needN โดยที่ 0 ≤ needi ≤ 1014
อีก Q บรรทัด บรรทัดที่ j มีจำนวนเต็ม l r และ w ของการรดครั้งที่ j คั่นด้วยช่องว่าง โดยที่ 1 ≤ l ≤ r ≤ N และ 1 ≤ w ≤ 109
ข้อมูลออก
จำนวนครั้งที่น้อยที่สุด หรือ -1
5 5
3 5 2 0 4
1 3 2
2 5 3
4 5 1
1 1 1
2 4 74ดูคำใบ้
ถ้ารดครบ k ครั้งแรกแล้วทุกต้นได้น้ำพอ รดเพิ่มอีกครั้งก็ยังพอ เพราะน้ำมีแต่เพิ่ม คำตอบจึงเป็น k แรกที่ตอบ "ใช่" แบบหน้า 19.3 ส่วนการตรวจว่า k ครั้งแรกพอไหม ใช้ difference array ในหน้า 19.5 กับ k รายการแรก แล้วสะสมเทียบกับที่แต่ละต้นต้องการ ตรวจหนึ่งครั้งเป็น O(N + Q) และตรวจราว 18 ครั้ง ถ้ารดครบ Q ครั้งแล้วยังไม่พอ ตอบ -1 ถ้าทุกต้นต้องการ 0 ลิตร ตอบ 0 ปริมาณน้ำและความต้องการเกิน int
ดูเฉลย
#include <bits/stdc++.h>
using namespace std;
int n, q;
vector<long long> need;
vector<int> from_tree, to_tree, water;
bool enough_after(int rounds)
{
vector<long long> diff(n + 2, 0);
for (int j = 0; j < rounds; j++) {
diff[from_tree[j]] += water[j];
diff[to_tree[j] + 1] -= water[j];
}
long long current = 0;
for (int tree = 1; tree <= n; tree++) {
current += diff[tree];
if (current < need[tree]) {
return false;
}
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
need.assign(n + 1, 0);
for (int tree = 1; tree <= n; tree++) {
cin >> need[tree];
}
from_tree.resize(q);
to_tree.resize(q);
water.resize(q);
for (int j = 0; j < q; j++) {
cin >> from_tree[j] >> to_tree[j] >> water[j];
}
if (!enough_after(q)) {
cout << -1 << '\n';
return 0;
}
int lo = 0, hi = q;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (enough_after(mid)) {
hi = mid;
} else {
lo = mid + 1;
}
}
cout << lo << '\n';
return 0;
}