題目描述
給定一個長度為 \(N\) 的序列 \(A\),以及 \(Q\) 次詢問,每次詢問給定一個 \(x\)。
你可以執行以下操作任意次:
- 選擇一個 \(1\le i \le N\) 使得 \(A_i \ge x\)。
- 令 \(A_i \leftarrow A_i - x\)。
求 \(A\) 的最小中位數。
這裡中位數是 \(A\) 排序後的第 \(\lfloor \frac{n}{2}\rfloor+1\) 個元素。
思路
很顯然,令一個 \(A_i-x\) 一定不會使答案更劣,所以肯定會把所有操作進行到底,也就是 \(A_i\leftarrow A_i \bmod x\)。然後讓你求這種情況下的中位數。
首先對 \(A_i\) 的值做一個字首和。對於每個 \(x\),二分其中位數。
二分的 check 也很簡單,直接列舉 \(x\) 的倍數 \(y\),求 \(y\) 到 \(y+mid\) 有多少個數,如果總數 \(\ge \lfloor \frac{n}{2}\rfloor+1\) 則合法,否則不合法。
這樣的時間複雜度是 \(O(N\log^2 N)\),因為這裡面是一個調和級數,所以時間不會炸。
空間複雜度 \(O(N)\),時間複雜度 \(O(N \log^2 N + Q)\)。
程式碼
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int T, n, q, a[MAXN], sum[MAXN], ans[MAXN];
bool check(int x, int y) {
int res = 0;
for(int i = 0; i * x <= n; ++i) {
res += sum[min(n, i * x + y)] - (i * x - 1 >= 0 ? sum[i * x - 1] : 0);
}
return res >= n / 2 + 1;
}
int Binary_Search(int x) {
int l = 0, r = x - 1;
for(; l < r; ) {
int mid = (l + r) >> 1;
(check(x, mid) ? r = mid : l = mid + 1);
}
return l;
}
void Solve() {
cin >> n >> q;
for(int i = 1; i <= n; ++i) {
sum[i] = 0;
}
for(int i = 1; i <= n; ++i) {
cin >> a[i];
sum[a[i]]++;
}
for(int i = 1; i <= n; ++i) {
sum[i] += sum[i - 1];
}
for(int i = 1; i <= n; ++i) {
ans[i] = Binary_Search(i);
}
for(int i = 1, x; i <= q; ++i) {
cin >> x;
cout << ans[x] << " \n"[i == q];
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
for(cin >> T; T--; Solve()) {
}
return 0;
}