C. Almost All Multiples
C. Almost All Multiples
Given two integers and , a permutation of length is called funny if is a multiple of for all , , and .
Find the lexicographically minimal funny permutation, or report that no such permutation exists.
A permutation of length is an array consisting of each of the integers from to exactly once.
Let and be permutations of length . Then is lexicographically smaller than if in the first position where and differ, . A permutation is lexicographically minimal if it is lexicographically smaller than all other permutations.
Input
The input consists of multiple test cases. The first line contains an integer () — the number of test cases. The description of the test cases follows.
The only line of each test case contains two integers and (; ).
The sum of across all test cases does not exceed .
Output
For each test case, if the answer exists, output distinct integers () — the lexicographically minimal funny permutation . Otherwise, output .
Example
input
3 3 3 4 2 5 4
output
3 2 1 2 4 3 1 -1
Note
In the first test case, the permutation satisfies all the conditions: , , and:
- is a multiple of .
- is a multiple of .
In the second test case, the permutation satisfies all the conditions: , , and:
- is a multiple of .
- is a multiple of .
- is a multiple of .
We can show that these permutations are lexicographically minimal.
No such permutations exist in the third test case.
解题思路
首先序列(即)是满足条件的,现在交换一下和,那么序列就变成了,也是满足条件的。当我们选择下标,交换和(也就是把换到了下标的位置),这时如果有,那么得到的序列还是满足条件的(不一定是字典序最小)。如果,那么我们要往后找到一个的倍数放到这个位置,这个数可以是 ,同理,如果选择了,那么还需要往后找到一个的倍数放到这个位置,以此类推,最终要把放到某个位置,由于最后这个位置也是的倍数,又因为这个位置不能整除(不包含因子),因此无解。
对于有解的情况为了得到字典序最小,我们可以先让,然后往后遍历找的第一个倍数(也就是),然后交换和,此时我们应该往后找到的第一个倍数(也就是),然后交换交换和,以此类推,直到无法交换为止。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 2e5 + 10; 5 6 int a[N]; 7 8 void solve() { 9 int n, m; 10 scanf("%d %d", &n, &m); 11 if (n % m) { 12 printf("-1\n"); 13 return; 14 } 15 a[1] = m, a[n] = 1; 16 for (int i = 2; i < n; i++) { 17 a[i] = i; 18 } 19 if (n != m) a[m] = n; // 如果a[1]=n那么已经是字典序最小了 20 for (int i = m + m, j = m; i < n; i += j) { // 当前放n的位置为j,因此要找j的倍数,即i+=j 21 if (n % i == 0) { // n可以放到i这个位置 22 swap(a[i], a[j]); 23 j = i; 24 } 25 } 26 for (int i = 1; i <= n; i++) { 27 printf("%d ", a[i]); 28 } 29 printf("\n"); 30 } 31 32 int main() { 33 int t; 34 scanf("%d", &t); 35 while (t--) { 36 solve(); 37 } 38 39 return 0; 40 }
补充另外一种做法。
对于,一定是无解的。假设放置在任意一个合法的下标位置(也就是是所在下标位置的倍数),那么这个位置原本的数就应该放置在前面某个合法位置,以此类推。可以发现在这个过程中被换出来的数都是的因子,不可能是的倍数(如果存在某个因子是的倍数那么也是的倍数,就矛盾了)。到最后必然会置换出一个质数然后这个质数就没地方放了(如果不是质数的话说明质因数分解会得到两个以上的质数的乘积,说明前面还存在这个数的因子,可以继续往前置换)。
因此对于有解的情况,此时下标位置空出来,为了得到字典序最小的序列,假设质因数分解,由于只能放在 的位置上,因此我们只能从小到大把这些数往前移一个位置,最后把放在 这个位置上。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 2e5 + 10; 5 6 int a[N]; 7 8 void solve() { 9 int n, m; 10 scanf("%d %d", &n, &m); 11 if (n % m) { 12 printf("-1\n"); 13 return; 14 } 15 a[1] = m, a[n] = 1; 16 for (int i = 2; i < n; i++) { 17 a[i] = i; 18 } 19 vector<int> fs; 20 int t = n / m; 21 for (int i = 2; i <= t / i; i++) { 22 while (t % i == 0) { 23 fs.push_back(i); 24 t /= i; 25 } 26 } 27 if (t > 1) fs.push_back(t); 28 for (int i = 0, j = m; i < fs.size(); i++) { 29 a[j] = j * fs[i]; 30 j *= fs[i]; 31 } 32 for (int i = 1; i <= n; i++) { 33 printf("%d ", a[i]); 34 } 35 printf("\n"); 36 } 37 38 int main() { 39 int t; 40 scanf("%d", &t); 41 while (t--) { 42 solve(); 43 } 44 45 return 0; 46 }
参考资料
Codeforces Round #836 (Div. 2) Editorial:https://codeforces.com/blog/entry/109438
Codeforces Round #836 (Div. 2) A-D :https://www.cnblogs.com/BlankYang/p/16928287.html
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16927719.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2021-11-26 关于KMP算法中模式串的移动不会产生漏解的证明