區間找最值的二分與字首和
阿新 • • 發佈:2022-05-30
《1.題目一--最佳牛欄問題》
題目連結:https://www.acwing.com/problem/content/104/
、
用到的基本思想:二分,字首和,平均值的特殊處理 這個題目的題意是,給出N塊連續的田地,田地上有牛; 我們想要在這個給出的一系列田地中找出連續的田地(連續的意思是按照題目給出的如 1 2 3 4 5 6 7 ,2 3 4 是連續的,而1 3 5不是連續的)上牛數的最大平均值 1.由這裡N《=1e5,應該想到用二分找答案,然後去判斷這個二分出來的答案對不對; 於是題目變成了,我找到了答案mid,是否能夠找到一段平均值》=mid; 如果找到了那麼還有提升空間l=mid+1; 否則 r=mid; 2.那麼如何去判斷這個最大平均值呢? 在一個區間上求值,第一個想到的是就是字首和; 但是這裡是平均值,有除數; 可以讓這題目給出的數值都-mid,然後轉化為字首和新得的,看某一區間是否》=0 如果》=0,那麼說明這一段區間》=mid; 3.當然這個是有長度要求的,即區間長度》=f; 我們可以開一個變數minv,一直維護其最小 由sum[k]-sum[minv]可以得到最大的
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 using namespacestd; 5 const int N = 1e5 + 10; 6 int n, f; 7 double sum[N]; 8 double num[N]; 9 bool check(double mid) 10 { 11 for (int i = 1; i <= n; i++) 12 sum[i] = sum[i - 1] + (num[i]-mid); 13 double minv=0; 14 for (int i=f;i<=n;i++) 15 { 16 minv=min(minv,sum[i-f]); 17 if(sum[i]-minv>=0) return true; 18 } 19 return false; 20 } 21 int main() 22 { 23 scanf("%d%d", &n, &f); 24 for (int i = 1; i <= n; i++) 25 scanf("%lf", &num[i]); 26 double l = 0, r = 2000; 27 while (r - l > 1e-6) 28 { 29 double mid = (l + r) / 2; 30 if (check(mid)) 31 l = mid; 32 else 33 r = mid; 34 } 35 printf("%d",int(r*1000)); 36 return 0; 37 }
《2.題目2--防線》
題目連結:https://www.acwing.com/problem/content/description/122/
120. 防線 達達學習數學競賽的時候受盡了同仁們的鄙視,終於有一天......受盡屈辱的達達黑化成為了黑暗英雄怪獸達達。 就如同中二漫畫的情節一樣,怪獸達達打算毀掉這個世界。 數學競賽界的精英 lqr 打算阻止怪獸達達的陰謀,於是她集合了一支由數學競賽選手組成的超級行動隊。 由於隊員們個個都智商超群,很快,行動隊便來到了怪獸達達的黑暗城堡的下方。 但是,同樣強大的怪獸達達在城堡周圍佈置了一條“不可越過”的堅固防線。 防線由很多防具組成,這些防具分成了 N 組。 我們可以認為防線是一維的,那麼每一組防具都分佈在防線的某一段上,並且同一組防具是等距離排列的。 也就是說,我們可以用三個整數 S, E 和 D 來描述一組防具,即這一組防具佈置在防線的 S,S+D,S+2D,…,S+KD(K∈Z,S+KD≤E,S+(K+1)D>E)位置上。 黑化的怪獸達達設計的防線極其精良。 如果防線的某個位置有偶數個防具,那麼這個位置就是毫無破綻的(包括這個位置一個防具也沒有的情況,因為 0 也是偶數)。 只有有奇數個防具的位置有破綻,但是整條防線上也最多隻有一個位置有奇數個防具。 作為行動隊的隊長,lqr 要找到防線的破綻以策劃下一步的行動。 但是,由於防具的數量太多,她實在是不能看出哪裡有破綻。 作為 lqr 可以信任的學弟學妹們,你們要幫助她解決這個問題。 輸入格式 輸入檔案的第一行是一個整數 T,表示有 T 組互相獨立的測試資料。 每組資料的第一行是一個整數 N。 之後 N 行,每行三個整數 Si,Ei,Di,代表第 i 組防具的三個引數,資料用空格隔開。 輸出格式 對於每組測試資料,如果防線沒有破綻,即所有的位置都有偶數個防具,輸出一行 "There's no weakness."(不包含引號) 。 否則在一行內輸出兩個空格分隔的整數 P 和 C,表示在位置 P 有 C 個防具。當然 C 應該是一個奇數。 資料範圍 防具總數不多於108, Si≤Ei, 1≤T≤5, N≤200000, 0≤Si,Ei,Di≤231−1 輸入樣例: 3 2 1 10 1 2 10 1 2 1 10 1 1 10 1 4 1 10 1 4 4 1 1 5 1 6 10 1 輸出樣例: 1 1 There's no weakness. 4 3
思路是利用只有一個奇數, 1.只有一個奇數說明,我可以利用二分法l=0,r=0x3f3f3f3f; 去列舉在哪裡; 因為如果那個奇數在l-mid中,則l-mid這個區間的防具總和為奇數, 反之是奇數在mid+1-r; 2.如何求l-mid中的防具總和呢? 字首和 我們算0-mid與0-(l-1)的防具總和,相減為l-mid這個區間的防具總和; 如何求0-x中的防具總和呢? 遍歷每一組,注意我們是求0-x中的防具總和,是很容易的 for (int i=1;i<=n;i++){ if (x<si) ans+=0; else ans+=(min(x,ei)-si)/di+1; }
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const int N = 1e5 + 10; 6 struct node 7 { 8 int s, e, d; 9 } zhu[N]; 10 int t, n; 11 long long sum(int x) 12 { 13 long long ans = 0; 14 for (int i = 1; i <= n; i++) 15 { 16 if (x < zhu[i].s) 17 ans += 0; 18 else 19 { 20 ans += (min(x, zhu[i].e) - zhu[i].s) / zhu[i].d + 1; 21 } 22 } 23 return ans; 24 } 25 bool check(int l, int mid) 26 { 27 if ((sum(mid) - sum(l - 1)) % 2) 28 return true; 29 else 30 return false; 31 } 32 int main() 33 { 34 scanf("%d", &t); 35 while (t--) 36 { 37 scanf("%d", &n); 38 int l = 0, r = 1e9; 39 for (int i = 1; i <= n; i++) 40 { 41 scanf("%d%d%d", &zhu[i].s, &zhu[i].e, &zhu[i].d); 42 l = min(zhu[i].s, l); 43 r = max(zhu[i].e, r); 44 } 45 while (r > l) 46 { 47 int mid = (l + r) >> 1; 48 if (check(l, mid)) 49 r = mid; 50 else 51 l = mid + 1; 52 } 53 long long ans = sum(r) - sum(r - 1); 54 if (ans % 2) 55 { 56 printf("%d %lld\n", r, ans); 57 } 58 else 59 { 60 printf("There's no weakness.\n"); 61 } 62 } 63 return 0; 64 }這個程式碼有問題是過不了的,但是思路是對的