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 ai is the smartness of the i-th (1in) student.

There are m topics 1,2,3,,m from which the quiz questions will be formed. The i-th student is considered proficient in a topic T if (aimodT)=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 (1t104). The description of the test cases follows.

The first line of each test case contains n and m (1n,m105).

The second line of each test case contains n integers a1,a2,,an (1ai105).

It is guaranteed that the sum of n over all test cases does not exceed 105.

It is guaranteed that the sum of m over all test cases does not exceed 105.

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 2m, 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,,7.

 

解题思路

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

  为了知道每个ai对应1m中的哪些数,因此需要对ai分解约数。如果直接用试除法那么时间复杂度为O(nA),其中Aai中的最大值,最大可以取到105,因此会超时。注意到实际上我们只需要找出各个ai中值不超过m的约数,因此可以反过来枚举1m,假设为i,再迭代i的倍数j (jA),那么j就包含约数i,时间复杂度为O(AlogA)

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

  AC代码如下,时间复杂度为O(AlogA+128nlogA)

复制代码
 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]完全覆盖了1m的最靠右的位置,当i往右移动时j也只会往右移动,具有单调性。

  AC代码如下,时间复杂度为O(AlogA+128n)

复制代码
 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

posted @   onlyblues  阅读(278)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示