Codeforces Round #768 (Div. 2) D. Range and Partition // 思维 + 贪心 + 二分查找

The link to problem:Problem - D - Codeforces
 
D. Range and Partition 
time limit per test: 2 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output

Given an array a of n integers, find a range of values [x,y(xy), and split a into exactly k (1kn) 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 (1lrn).
  • 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 xaiy.

Print any solution that minimizes yx.

Input

The input consists of multiple test cases. The first line contains a single integer tt (1t3104) — the number of test cases. Description of the test cases follows.

The first line of each test case contains two integers n and k (1kn2105) — 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 (1ain) 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 2105.

Output

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 (1lirin) — the limits of the i-th subarray.

You can print the subarrays in any order.

Example
input
3
2 1
1 2
4 2
1 2 2 2
11 3
5 5 5 1 5 5 1 5 5 5 1
output
1 2
1 2
2 2
1 3
4 4
5 5
1 1
2 2
3 11
Note

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 }

 

 

 

 

 

 

 

 

 

posted @ 2022-01-30 15:00  Jakon  阅读(124)  评论(0)    收藏  举报