C. Quiz Master
C. Quiz Master
A school has to decide on its team for an international quiz. There are students in the school. We can describe the students using an array where is the smartness of the -th () student.
There are topics from which the quiz questions will be formed. The -th student is considered proficient in a topic if . 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 (). The description of the test cases follows.
The first line of each test case contains and ().
The second line of each test case contains integers ().
It is guaranteed that the sum of over all test cases does not exceed .
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, print the answer on a new line. If there is no solution, output .
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 and , and . Thus, there is no student with smartness divisible by . Since , there is no way to choose a team.
In the second test case, we can select the participant with smartness to be the only one on the team. This way the team will be collectively proficient in both topics and .
In the third test case, consider the team with participants of smartnesses . This way the team will be collectively proficient in all topics .
解题思路
一看完题就想到二分答案。实际上答案是满足二段性的,假设答案是,那么很显然小于的值是取不到的,否则就矛盾了;对于大于等于的值,必然存在一种方案使得最大值与最小值的差不超过该值(因为已经必然存在差值不超过的方案)。
为了知道每个对应中的哪些数,因此需要对分解约数。如果直接用试除法那么时间复杂度为,其中是中的最大值,最大可以取到,因此会超时。注意到实际上我们只需要找出各个中值不超过的约数,因此可以反过来枚举,假设为,再迭代的倍数,那么就包含约数,时间复杂度为。
在函数中,对于二分出的,我们要看看是否存在一种方案使得差值不超过且完全覆盖。因此对数组从小到大排序,然后用双指针算法,枚举右端点,指针表示不超过的最靠左的位置。在这个区间中的数都应该选择,因为选择的越多能覆盖的数就越多。可以发现当往右移动,也只会往右移动,具有单调性。在迭代的时候开个哈希表来维护中每个数被覆盖了多少次,再开个变量来维护覆盖了多少种数,当就说明存在一种方案。在枚举到时,把的所有约数在哈希表中计数加,相应的当要往右移动,把的所有约数在哈希表中计数减。在不超过的数中,一个数最多有个约数,因此函数的时间复杂度为。
AC代码如下,时间复杂度为:
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 }
看了一下官方题解,实际上可以直接双指针,不需要二分。一样枚举右端点,这里定义为区间完全覆盖了的最靠右的位置,当往右移动时也只会往右移动,具有单调性。
AC代码如下,时间复杂度为:
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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17064206.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效