Codeforces Round #768 (Div. 2) D. Range and Partition // 思维 + 贪心 + 二分查找
Given an array a of n integers, find a range of values [x,y] (x≤y), and split a into exactly k (1≤k≤n) subarrays in such a way that:
- Each subarray is formed by several continuous elements of aa, that is, it is equal to al,al+1,…,ar for some l and r (1≤l≤r≤n).
- Each element from aa belongs to exactly one subarray.
- In each subarray the number of elements inside the range [x,y] (inclusive) is strictly greater than the number of elements outside the range. An element with index i is inside the range [x,y] if and only if x≤ai≤y.
Print any solution that minimizes y−x.
The input consists of multiple test cases. The first line contains a single integer tt (1≤t≤3⋅104) — the number of test cases. Description of the test cases follows.
The first line of each test case contains two integers n and k (1≤k≤n≤2⋅105) — the length of the array a and the number of subarrays required in the partition.
The second line of each test case contains nn integers a1,a2,…,an (1≤ai≤n) where ai is the i-th element of the array.
It is guaranteed that the sum of n over all test cases does not exceed 2⋅105.
For each test case, print k+1 lines.
In the first line, print x and y — the limits of the found range.
Then print k lines, the i-th should contain li and ri (1≤li≤ri≤n) — the limits of the i-th subarray.
You can print the subarrays in any order.
3 2 1 1 2 4 2 1 2 2 2 11 3 5 5 5 1 5 5 1 5 5 5 1
1 2 1 2 2 2 1 3 4 4 5 5 1 1 2 2 3 11
In the first test, there should be only one subarray, which must be equal to the whole array. There are 2 elements inside the range [1,2] and 0 elements outside, if the chosen range is [1,1], there will be 1 element inside (a1) and 1 element outside (a2), and the answer will be invalid.
In the second test, it is possible to choose the range [2,2], and split the array in subarrays (1,3) and (4,4), in subarray (1,3) there are 2 elements inside the range (a2 and a3) and 1 element outside (a1), in subarray (4,4) there is only 1 element (a4), and it is inside the range.
In the third test, it is possible to choose the range [5,5], and split the array in subarrays (1,4), (5,7) and (8,11), in the subarray (1,4) there are 3 elements inside the range and 1 element outside, in the subarray (5,7) there are 2 elements inside and 1 element outside and in the subarray (8,11) there are 3 elements inside and 1 element outside.
题目大意:
确定x,y,满足条件:使得数组中元素可以分为正好k个子数组,使得每个子数组中,在[x,y]内的元素个数严格大于在[x,y]之外的元素个数。
要求y-x的值最小。
思路:思维 + 贪心 + 二分查找
首先,当x,y确定时,要保证可以分为符合题意的k个数组,只需要满足以下条件:数组中在[x,y]内的元素个数cnt1,以及在范围外的元素个数cnt2,cnt1 - cnt2 >= k。
证明:不妨设b[i], 当a[i]在区间内时,b[i] = 1, 否则b[i] = -1, 对于数组b求前缀和:当pre[i] = 1时,可以第1次分割数组;当pre[i] = 2时,可以第2次分割数组;……;当pre[i] = k时,可以第k次分割数组。那么,只要pre[n] >= k,这样的x,y就必定满足题意。
接下来,考虑如何如何使 y - x 的值最小:将原数组复制一份并排序(得到数组b),枚举左边界x,二分查找符合(1)的右边界y,并动态更新x,y, 使得y - x尽量小。设数组b中大于等于x的第一个下标为 l,则查找到的下标 r,应满足:(r - l + 1) - [ n - (r - l + 1) ] >= k。
代码:
1 //Jakon: 2 #include <bits/stdc++.h> 3 #define int long long 4 using namespace std; 5 const int N = 200010; 6 7 int test, n, k, a[N], b[N], x, y; 8 9 void solve() 10 { 11 sort(b+1, b+1+n); // 排序后可二分查找 12 int ans = b[n] - b[1] + 1; 13 x = b[1], y = b[n]; 14 for(int i = b[1]; i <= b[n]; i++) 15 { 16 int l = lower_bound(b+1, b+1+n, i) - b; 17 int len = (n + k + 1) >> 1; // len - (n-len) >= k 18 if(l+len-1 > n) continue; 19 int xx = b[l], yy = b[l+len-1]; 20 if(yy - xx < ans) ans = yy - xx, x = xx, y = yy; 21 } 22 int now = 0, l = 1, r = 0, cnt = 0; 23 printf("%lld %lld\n", x, y); 24 for(int i = 1; i <= n; i++) 25 { 26 r ++; 27 if(x <= a[i] && a[i] <= y) now += 1; 28 else now -= 1; 29 if(now == 1 && cnt != k-1) 30 { 31 printf("%lld %lld\n", l, r); 32 now = 0, l = i + 1, cnt ++; 33 } 34 } 35 printf("%lld %lld\n", l, r); 36 cout << endl; 37 } 38 39 signed main() 40 { 41 cin >> test; 42 while(test--) 43 { 44 cin >> n >> k; 45 for(int i = 1; i <= n; i++) scanf("%lld", &a[i]), b[i] = a[i]; 46 solve(); 47 } 48 49 return 0; 50 }