[hihoCoder 1384]Genius ACM
Description
題庫鏈接
給定一個整數 \(M\),對于任意一個整數集合 \(S\),定義“校驗值”如下:
從集合 \(S\) 中取出 \(M\) 對數(即 \(2\times M\) 個數,不能重復使用集合中的數,如果 \(S\) 中的整數不夠 \(M\) 對,則取到不能取為止),使得“每對數的差的平方”之和最大,這個最大值就稱為集合 \(S\) 的“校驗值”。
現在給定一個長度為 \(N\) 的數列 \(A\) 以及一個整數 \(k\)。我們要把 \(A\) 分成若干段,使得每一段的“校驗值”都不超過 \(k\)。求最少需要分成幾段。 多測,測試組數為 \(T\)。
\(T\leq 12,1\leq n,m\leq 5\times 10^5,0\leq k\leq 10^{18},0\leq P_i\leq 2^{20}\)
Solution
先考慮如何求“校驗值”。思路是當題述結果最大時,應該要求最大值和最小值一組;次大值和次小值一組……
我們以 \(4\) 個數為例,如 \(a,b,c,d\),其中 \(a\leq b\leq c\leq d\) 。那么 \((d-a)^2+(c-b)^2=a^2+b^2+c^2+d^2-2ad-2bc\),\((b-a)^2+(d-c)^2=a^2+b^2+c^2+d^2-2ab-2cd\)。容易發現 \(ad+bc<ab+cd\),故前者更優。更多數時用數學歸納法可以證明。
其次,另外一個要點是當一個區間左端點固定時右端點要盡可能往右取。
注意到這兩點,我們左端點從 \(1\) 開始,向右找到最遠的符合條件的右端點,劃分為一段。再接著固定左端點,繼續尋找。這樣即可統計出答案。假設我們右端點是枚舉得到的,記答案(最少段數)為 \(ans\),那么本算法的復雜度約為 \(O\left(ans\times\left(\frac{n}{ans}\right)^2\log\frac{n}{ans}\right)=O\left(n^2\times\frac{\log\frac{n}{ans}}{ans}\right)\)。
若答案趨為 \(1\),復雜度趨為 \(O\left(n^2\log n\right)\)。顯然枚舉右端點的思路是過不了此題的。而計算“校驗值”的復雜度是無法再優化的,考慮如何快速尋找右端點。
一個思路是二分右端點,其余同上,由主定理可以證明復雜度是 \(O(n\log^2 n)\) 的。不過可惜的是出題人把這個算法卡掉了。
既然二分不行,我們考慮用倍增的思路來求右端點,具體思路是首先設倍增 \(len\) 長度為 1,若右端點為 \(r\),判斷若端點 \(r+len\) 符合條件。則將右端點賦值為 \(r+len\) 并且將 \(len\) 倍增,繼續討論;若不符合條件則將 \(len\) 縮短一半,繼續討論。其實該算法的復雜度與二分是一致的,不過只能寫倍增才能過此題。
另外,不論二分還是倍增,排序直接 \(\text{sort}\) 依舊過不了。注意到這樣一個小技巧,如果上一個枚舉的右端點為 \(r\),這一次右端點為 \(r'\),那么可以知道區間 \((l,r)\) 是有序的,那么我們只需排序 \((r,r')\) 的區間,之后將兩個部分歸并即可使整個 \((l,r')\) 有序。
Code
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 5e5+5; void gi(int &x) {x = 0; char ch = getchar();while (ch < '0' || ch > '9') ch = getchar();while (ch >= '0' && ch <= '9') x = x*10+ch-'0', ch = getchar(); }int t, n, m, p[N], a[N], kl, kr, tmp[N]; ll k;void merge(int l, int r, int x, int y) {int i = l, j = x, k = l;while (i <= r && j <= y)if (a[i] < a[j]) tmp[k++] = a[i++];else tmp[k++] = a[j++];while (i <= r) tmp[k++] = a[i++];while (j <= y) tmp[k++] = a[j++];for (int i = l; i <= y; i++) a[i] = tmp[i]; } bool judge(int l, int r) {if (kl == l && r > kr) {for (int i = kr+1; i <= r; i++) a[i] = p[i];sort(a+kr+1, a+r+1);merge(l, kr, kr+1, r); kr = r;} else {for (int i = l; i <= r; i++) a[i] = p[i];sort(a+l, a+r+1);kl = l, kr = r;}int t = 0; ll cnt = 0;while (t < m && l < r) {cnt += 1ll*(a[r]-a[l])*(a[r]-a[l]);++l, ++t, --r;if (cnt > k) return false;}return cnt <= k; } void work() {scanf("%d%d%lld", &n, &m, &k);for (int i = 1; i <= n; i++) gi(p[i]);int l = 1, r = 1, ans = 0, len, k;while (l <= n) {len = 1, k = l;while (len) {if (r+len <= n && judge(l, r+len)) {r += len;k = r, len <<= 1;}else len >>= 1;}l = r = k+1, ++ans;}printf("%d\n", ans); } int main() {scanf("%d", &t);while (t--) work();return 0; }轉載于:https://www.cnblogs.com/NaVi-Awson/p/11167203.html
總結
以上是生活随笔為你收集整理的[hihoCoder 1384]Genius ACM的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: firewalld防火墙简介
- 下一篇: Java 并发框架Disruptor(七