Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Start by asking which positions are absolutely forced. Value is useless for every constrained index because for all .
If is prime and , then the only number from to divisible by is itself. So position has no valid choice except .
Now flip the problem: can every other number be moved away from itself? If several indices all share a prime factor , you can cyclically rotate their values and every gcd stays good.
The clean grouping is by largest prime factor. For every prime , the group is never a singleton because it contains at least and .
Process primes from large to small. For each prime , take all still-unassigned multiples of and rotate them. This is exactly grouping by largest prime factor, and the only skipped singletons are the forced large primes.
The trap here is thinking we need some clever matching algorithm. We don't. The graph has a very polite number-theory structure, so a simple rotation construction does the whole job.
Forced Fixed Points
First, is forced.
For every constrained position , we need . But , so value cannot appear at any position . Therefore in every valid permutation. Fixed point number one. Rude, but fair.
Now take a prime . At position , the value must share a factor with . Since is prime, that means must be divisible by . But among , the only multiple of is itself, because .
So every prime is also forced to be fixed.
That gives a lower bound:
fixed points. If we can build a permutation with exactly those fixed points, we are done.
How To Move Everything Else
Suppose we have a bunch of numbers that all share some prime factor :
If we cyclically rotate their values like
then every position still receives a number divisible by , so every gcd is at least .
Also, if , nobody in that group stays fixed. Nice.
So the whole problem becomes: partition all non-forced numbers into groups of size at least , where every group has a common prime factor.
Largest Prime Factor Groups
For each number , assign it to the group of its largest prime factor.
Example: goes to prime , because . Number goes to prime .
Now look at a prime . Its group contains at least:
Both are at most , and both have largest prime factor . So this group has size at least , meaning we can rotate it with no fixed points.
What about primes ? Their group is just , exactly the forced fixed case we already proved unavoidable. No magic can save those guys.
Implementation Trick
Instead of explicitly computing largest prime factors, process primes in descending order.
For a prime , collect all multiples of that have not been used yet. Since larger primes were handled earlier, these are exactly the numbers whose largest prime factor is .
If the collected list has size at least , cyclically rotate it and mark all its numbers used.
If the collected list has size , leave it alone. That only happens for forced large primes.
Initialize for all , so untouched forced positions stay fixed automatically.
Correctness Proof
Every assigned group is made of multiples of the same prime . After rotating the group, each index receives another multiple of , so .
The groups are disjoint because once a number is used, it is never collected again. Rotating each group preserves exactly the same set of values inside that group, and untouched values stay at themselves, so the final array is a permutation.
No rotated group creates a fixed point because every rotated group has size at least .
The only untouched numbers are and primes greater than . We already proved every valid permutation must fix exactly those positions. Therefore our construction has the minimum possible number of fixed points.
Complexity
Sieve primes up to the maximum once. For each test case, scanning multiples over all primes costs about , easily fine for total .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
vector<int> ns(t);
int mx = 0;
for (int &n : ns) {
cin >> n;
mx = max(mx, n);
}
vector<bool> isPrime(mx + 1, true);
if (mx >= 0) isPrime[0] = false;
if (mx >= 1) isPrime[1] = false;
vector<int> primes;
for (int i = 2; i <= mx; i++) {
if (!isPrime[i]) continue;
primes.push_back(i);
if (1LL * i * i <= mx) {
for (ll j = 1LL * i * i; j <= mx; j += i) {
isPrime[(int)j] = false;
}
}
}
for (int n : ns) {
vector<int> ans(n + 1), used(n + 1, 0);
iota(ans.begin(), ans.end(), 0);
int upto = upper_bound(primes.begin(), primes.end(), n) - primes.begin();
for (int id = upto - 1; id >= 0; id--) {
int p = primes[id];
vector<int> group;
for (int x = p; x <= n; x += p) {
if (!used[x]) group.push_back(x);
}
if ((int)group.size() < 2) continue;
int m = group.size();
for (int i = 0; i < m; i++) {
ans[group[i]] = group[(i + 1) % m];
used[group[i]] = 1;
}
}
for (int i = 1; i <= n; i++) {
if (i > 1) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
vector<int> ns(t);
int mx = 0;
for (int &n : ns) {
cin >> n;
mx = max(mx, n);
}
vector<bool> isPrime(mx + 1, true);
if (mx >= 0) isPrime[0] = false;
if (mx >= 1) isPrime[1] = false;
vector<int> primes;
for (int i = 2; i <= mx; i++) {
if (!isPrime[i]) continue;
primes.push_back(i);
if (1LL * i * i <= mx) {
for (ll j = 1LL * i * i; j <= mx; j += i) {
isPrime[(int)j] = false;
}
}
}
for (int n : ns) {
vector<int> ans(n + 1), used(n + 1, 0);
iota(ans.begin(), ans.end(), 0);
int upto = upper_bound(primes.begin(), primes.end(), n) - primes.begin();
for (int id = upto - 1; id >= 0; id--) {
int p = primes[id];
vector<int> group;
for (int x = p; x <= n; x += p) {
if (!used[x]) group.push_back(x);
}
if ((int)group.size() < 2) continue;
int m = group.size();
for (int i = 0; i < m; i++) {
ans[group[i]] = group[(i + 1) % m];
used[group[i]] = 1;
}
}
for (int i = 1; i <= n; i++) {
if (i > 1) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
return 0;
}