C. Quiz Master

C. Quiz Master

A school has to decide on its team for an international quiz. There are $n$ students in the school. We can describe the students using an array $a$ where $a_i$ is the smartness of the $i$-th ($1 \le i \le n$) student.

There are $m$ topics $1, 2, 3, \ldots, m$ from which the quiz questions will be formed. The $i$-th student is considered proficient in a topic $T$ if $(a_i \bmod T) = 0$. Otherwise, he is a rookie in that topic.

We say that a team of students is collectively proficient in all the topics if for every topic there is a member of the team proficient in this topic.

Find a team that is collectively proficient in all the topics such that the maximum difference between the smartness of any two students in that team is minimized. Output this difference.

Input

Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 10^4$). The description of the test cases follows.

The first line of each test case contains $n$ and $m$ ($1 \le n,m \le 10^5$).

The second line of each test case contains $n$ integers $a_1, a_2, \ldots, a_n$ ($1 \le a_i \le 10^5$).

It is guaranteed that the sum of $n$ over all test cases does not exceed $10^5$.

It is guaranteed that the sum of $m$ over all test cases does not exceed $10^5$.

Output

For each test case, print the answer on a new line. If there is no solution, output $-1$.

Example

input

3
2 4
3 7
4 2
3 7 2 9
5 7
6 4 3 5 7

output

-1
0
3

Note

In the first test case, we have participants with smartnesses $3$ and $7$, and $m = 4$. Thus, there is no student with smartness divisible by $2$. Since $2 \leq m$, there is no way to choose a team.

In the second test case, we can select the participant with smartness $2$ to be the only one on the team. This way the team will be collectively proficient in both topics $1$ and $2$.

In the third test case, consider the team with participants of smartnesses $4, 5, 6, 7$. This way the team will be collectively proficient in all topics $1, 2, \ldots, 7$.

 

解题思路

  一看完题就想到二分答案。实际上答案是满足二段性的,假设答案是$\text{ans}$,那么很显然小于$\text{ans}$的值是取不到的,否则就矛盾了;对于大于等于$\text{ans}$的值,必然存在一种方案使得最大值与最小值的差不超过该值(因为已经必然存在差值不超过$\text{ans}$的方案)。

  为了知道每个$a_i$对应$1 \sim m$中的哪些数,因此需要对$a_i$分解约数。如果直接用试除法那么时间复杂度为$O(n \cdot \sqrt{A})$,其中$A$是$a_i$中的最大值,最大可以取到${10}^5$,因此会超时。注意到实际上我们只需要找出各个$a_i$中值不超过$m$的约数,因此可以反过来枚举$1 \sim m$,假设为$i$,再迭代$i$的倍数$j \ (j \leq A)$,那么$j$就包含约数$i$,时间复杂度为$O(A \log{A})$。

  在$\text{check}$函数中,对于二分出的$\text{mid}$,我们要看看是否存在一种方案使得差值不超过$\text{mid}$且完全覆盖$1 \sim m$。因此对数组$a$从小到大排序,然后用双指针算法,枚举右端点$i$,指针$j$表示$a_j - a_i$不超过$\text{mid}$的最靠左的位置。在$[j, i]$这个区间中的数都应该选择,因为选择的越多能覆盖的数就越多。可以发现当$i$往右移动,$j$也只会往右移动,具有单调性。在迭代的时候开个哈希表来维护$1 \sim m$中每个数被覆盖了多少次,再开个变量$s$来维护覆盖了多少种数,当$s = m$就说明存在一种方案。在枚举到$a_i$时,把$a_i$的所有约数在哈希表中计数加$1$,相应的当$j$要往右移动,把$a_j$的所有约数在哈希表中计数减$1$。在不超过${10}^5$的数中,一个数最多有$128$个约数,因此$\text{check}$函数的时间复杂度为$O(128 \cdot n)$。

  AC代码如下,时间复杂度为$O(A \log{A} + 128 \cdot n\log{A})$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e5 + 10;
 5 
 6 int n, m;
 7 int a[N];
 8 vector<int> ds[N];
 9 bool vis[N];
10 int cnt[N];
11 
12 bool check(int mid) {
13     memset(cnt, 0, sizeof(cnt));
14     int s = 0;
15     for (int i = 0, j = 0; i < n; i++) {
16         for (auto &x : ds[a[i]]) {  // 把a[i]的约数都加到哈希表中
17             if (++cnt[x] == 1) s++;
18         }
19         while (a[i] - a[j] > mid) { // 插值超过mid
20             for (auto &x : ds[a[j]]) {  // 从哈希表中删除a[j]的所有约数
21                 if (--cnt[x] == 0) s--;
22             }
23             j++;
24         }
25         if (s == m) return true;    // 覆盖1~m,存在一组解
26     }
27     return false;
28 }
29 
30 void solve() {
31     scanf("%d %d", &n, &m);
32     memset(vis, 0, sizeof(vis));
33     for (int i = 0; i < n; i++) {
34         scanf("%d", a + i);
35         vis[a[i]] = true;
36         ds[a[i]].clear();
37     }
38     sort(a, a + n);
39     for (int i = 1; i <= m; i++) {  // 枚举1~m,即不超过m的约数
40         for (int j = i; j <= a[n - 1]; j += i) {    // 枚举i的倍数j,j含有约数i
41             if (vis[j]) ds[j].push_back(i); // j要存在
42         }
43     }
44     int l = 0, r = a[n - 1] - a[0] + 1;
45     while (l < r) {
46         int mid = l + r >> 1;
47         if (check(mid)) r = mid;
48         else l = mid + 1;
49     }
50     printf("%d\n", l <= a[n - 1] - a[0] ? l : -1);  // 当l == a[n-1]-a[0]+1,表示选择所有数都无法覆盖1~m,无解
51 }
52 
53 int main() {
54     int t;
55     scanf("%d", &t);
56     while (t--) {
57         solve();
58     }
59     
60     return 0;
61 }

  看了一下官方题解,实际上可以直接双指针,不需要二分。一样枚举右端点$i$,这里$j$定义为区间$[j, i]$完全覆盖了$1 \sim m$的最靠右的位置,当$i$往右移动时$j$也只会往右移动,具有单调性。

  AC代码如下,时间复杂度为$O(A \log{A} + 128 \cdot n)$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e5 + 10;
 5 
 6 int a[N];
 7 vector<int> ds[N];
 8 bool vis[N];
 9 int cnt[N];
10 
11 void solve() {
12     int n, m;
13     scanf("%d %d", &n, &m);
14     memset(vis, 0, sizeof(vis));
15     for (int i = 0; i < n; i++) {
16         scanf("%d", a + i);
17         vis[a[i]] = true;
18         ds[a[i]].clear();
19     }
20     sort(a, a + n);
21     for (int i = 1; i <= m; i++) {
22         for (int j = i; j <= a[n - 1]; j += i) {
23             if (vis[j]) ds[j].push_back(i);
24         }
25     }
26     memset(cnt, 0, sizeof(cnt));
27     int s = 0, ret = 2e9;
28     for (int i = 0, j = 0; i < n; i++) {
29         for (auto &x : ds[a[i]]) {
30             if (++cnt[x] == 1) s++;
31         }
32         if (s == m) {   // [j, i]已经覆盖了1~m
33             while (s == m) {    // j往右移动
34                 for (auto &x : ds[a[j]]) {
35                     if (--cnt[x] == 0) s--;
36                 }
37                 j++;
38             }
39             j--;    // j要减1,因为退出时[j, i]没有完全覆盖1~m,此时[j-1, i]才完全覆盖了1~m
40             for (auto &x : ds[a[j]]) {  // a[j-1]的约数还要加到哈希表中
41                 if (++cnt[x] == 1) s++;
42             }
43             ret = min(ret, a[i] - a[j]);
44         }
45     }
46     printf("%d\n", ret == 2e9 ? -1 : ret);
47 }
48 
49 int main() {
50     int t;
51     scanf("%d", &t);
52     while (t--) {
53         solve();
54     }
55     
56     return 0;
57 }  

 

  最后,今天是2023/1/22,新年快乐 ^-^ !我记得去年春节的时候也发了篇博客qwq。

 

参考资料

  Codeforces Round #845 (Div. 2) and ByteRace 2023 Editorial:https://codeforces.com/blog/entry/111729

原文链接: https://www.cnblogs.com/onlyblues/p/17064206.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C. Quiz Master

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/313598

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 下午12:58
下一篇 2023年2月16日 下午12:58

相关推荐