Bob has given Alice a necklace as her birthday gift. That necklace has 𝑁N crystals, 𝑀M of which catches her fancy. Formally, crystals with labels of 1..𝑁1..N are strung sequentially in a circle. And those catching her fancy are labelled 𝑛1,𝑛2,...,𝑛𝑀n1,n2,...,nM.
Alice would like the necklace to be divided into 𝑀M pieces. She asked Bob to sever the necklace into 𝑀M pieces. Each piece should consist of a sequence of crystals as in the original necklace (i.e., the crystal labels are contiguous, except that 11 following 𝑁N is accepted) and should include one crystal catching Alice's fancy. Obviously, each crystal must belong to exactly one piece.
However, an excessively long piece of severed necklaces could not fit Alice's slim neck. So she wants to know, among all possible severings as requested, what the minimum length of the longest piece from a severed necklace is. She wants you to answer this question.
Input
The first line of the input data contains 2 integers 𝑁N and 𝑀M (1≤𝑀≤𝑁<10181≤M≤N<1018 and 𝑀≤106M≤106).
The second line contains 𝑀M integers, the 𝑖i-th of which represents 𝑛𝑖ni. The 𝑛𝑖ni's are given in ascending order.
Output
You need to output your answer in one line.
Example
input
Copy
10 4
2 5 6 8
output
Copy
3
Note
As for the example: You can sever the necklace into [1,3],[4,5],[6,7],[8,10][1,3],[4,5],[6,7],[8,10].
Considering huge scale of data, here is a way provided to help input faster:
看了題解沒看懂(出題人語文水平堪憂,后來自己像明白了還各種寫錯變量wa了十發,綳不住了
首先最大值最小的問題一眼二分,關鍵在於check函數怎么寫。首先一個貪心思路是對於從左到右對於每個特殊的寶石在滿足左邊的需求時盡可能往右取。比如對於題目樣例,如果二分到的值是2的話,一開始2作為左端點,盡可能往右取能取到[2,3]這個區間,此時第二個特殊的寶石是5,顯然4這個寶石沒法被第一個區間覆蓋,那么第一個區間的需求就是1,那么枚舉到5的時候,首先要滿足需求,所以第二個區間只能是[4,5]...如果最終能首尾接上則可行。但這么貪心顯然是錯的,比如對於樣例:
10 3
3 5 7
最優策略一定是5覆蓋4和6,然后3與7盡量向兩邊擴展,但上述貪心無法很好地利用中間的5。但這個思想可以啟發我們,最終的貪心策略不是能給則給,而是“滿足則不給,不滿足則盡可能少地給使之滿足”。同樣也是枚舉特殊寶石,對於第一個寶石,我們先讓它為長為x的段的右端點;然后從左往右遍歷特殊的寶石,設上一個區間的右端點為lst,當前特殊寶石位置為i,先盡量往左覆蓋再盡量往右覆蓋,如果能把上一個區間的右端點到當前特殊寶石這段區間都覆蓋的話就可以盡量往右覆蓋了,如果lst + 1 < i - x,說明盡量往左覆蓋的話已經不夠用了,此時需要從第一個區間開始,所有區間往右移動(當然有可能存在區間使得移動不起作用),看看能否滿足覆蓋i - 1到i這段區間,只要能覆蓋那么就只移動這段距離絕不多移動,如果移動到頭還不能覆蓋則check(x)應該返回false。遍歷一遍以后看看最后一個區間的右端點能否和第一個區間的左端點接上,如果接不上則返回false,否則返回true。那么還有一個問題,如何判斷是否還能移動呢?這時候可以維護兩個變量offset以及each_min_offset,offset記錄第一個段往右移動的距離,each_min_offset記錄的是當前整體能往右移動的距離,維護each_min_offset的關鍵思想就是用每一段的左端點到這段的特殊寶石的距離來更新each_min_offset(取最小)。
比如對於樣例:
10 3
2 5 9
假設二分的值為3,一開始第一段是[10, 1, 2], offset = 0, each_min_offset = 2(第一段的左端點最大為2,所以10到2可以移動兩步),lst = 2;枚舉到5時先往左取再往右,此時[3, 4, 5]已經用光了,且不需要整體移動,因此offset = 0, each_min_offset = 2, lst = 5;枚舉到9時,盡量往左取也只能取出來[7, 8, 9],無法覆蓋6,因此需要整體移動,此時each_min_of set = 2,可以往右最多移動兩步,但我們只讓它往右移動一步,所以offset = 1, each_min_offset = 1(此時當前段的左端點到當前特殊寶石的距離肯定大於each_min_offset了),lst = 9。遍歷完后,第一個區間左端點向右移動offset個距離,變為了1.此時最后一個區間的右端點是9,第一個區間的左端點是1,中間相差一個10,因此無法銜接,故check(3) = false。
具體細節見代碼。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <queue>
#define int long long
#define ll long long
#define pb push_back
using namespace std;
int n, m, M[1000005];
//6 3
//3 4 5
bool check(ll mid) {
ll offset = 0, lst = M[1];
int each_min_offset = mid - 1;
int len = mid;
int res = 0;
bool yuejie1 = 0, yuejie2 = 0;//判斷第一個區間的左端點和最后一個區間的右端點是否跨越了n到1
//第一個特殊的一開始作為第一段的最后一個
for(int i = 2; i <= m; i++) {
if(len >= M[i] - lst) {
res = len - (M[i] - lst);//能夠往右擴展的
lst = M[i] + res;
if(i == m) {
if(lst > n) {
lst %= n;
lst = min(lst, M[1] - 1);
yuejie1 = 1;
}
} else {
lst = min(lst, M[i + 1] - 1);//注意不能把特殊寶石也包含進去
}
each_min_offset = min(each_min_offset, M[i] - lst - 1);//這里要給each_min_offset取min進行更新
} else {
if(M[i] - M[i - 1] + 1 > 2 * len) return false;//湊不出來
int need = M[i] - lst - len;//需要整體移動的距離
if(need > each_min_offset) return false;//無法滿足
each_min_offset -= need;//最多能往右移動的距離需要減小
offset += need;
lst = M[i];
}
}
int lmst = lst, rmst;//這里的lmst是最后一個區間右端點的位置,rmst是第一個區間左端點的位置
if(M[1] - len + 1 + offset >= 1) rmst = M[1] - len + 1 + offset;
else {
yuejie2 = 1;
rmst = n + M[1] - len + 1 + offset;
}
//判斷能否接上,寫丑了QoQ
if(yuejie1 && yuejie2) {
return 1;
} else if(yuejie1) {
return lmst >= rmst - 1;
} else if(yuejie2) {
return lmst >= rmst - 1;
} else {
if(lmst == n && rmst == 1) return 1;
else return 0;
}
}
signed main() {
cin.tie(0);
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1; i <= m; i++) {
cin >> M[i];
}
if(m == 1) {
cout << n;
return 0;
}
ll l = 1, r = n, mid;
while(l < r) {
mid = (l + r) >> 1;
//cout << mid << endl;
if(check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l;
return 0;
}
// 20 3
// 4 7 10
//20 3
//5 6 7 8 9 ?
// 20 5
// 1 2 3 5 8