CF 2123F(div3) R1700
题意:
构造一个n的排列a,满足总有gcd(a[i], i) > 1, 要求最小化num(a[i] == i).
思路:
1.首先考虑什么情况下必须有a[i] = i,那么显然是i > n / 2且i是质数的情况下。
一开始的想法是i <= n / 2时令p[i] = i * 2, 但是发现问题并没有被简化多少。
2.从约束条件入手:gcd(a[i], i) > 1表明构造出来的a[i] 和 i 有 大于1的公因数, 那么我们是否可以转换角度,通过枚举因数入手?
3.我们注意到,所有含有因数x(x > 1)的i,我们把它们互相轮换,还是满足gcd > 1,并且 a[i] != i
4.为了避免重复,我们通过下标i的最小大于1的因数x进行划分。那么我们用A预处理最小因数,再用G对下标进行划分,然后在G内轮换即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
void solve() {
int n; cin>>n;
vector<int> a(n + 1);
iota(a.begin(), a.end(), 0);
vector<int> A(n + 1);
//这里对A的处理有点类似于埃氏筛的感觉
for(int i = 2; i <= n; ++i) {
if (A[i] == 0) {
for (int j = i; j <= n; j += i) {
A[j] = i;
}
}
}
vector<vector<int>> g(n + 1);
for (int i = 2; i <= n; ++i) {
g[A[i]].push_back(i);
}
// 注意,这里倒序会方便很多!
for (int i = n; i >= 2; --i) {
//仅当遇到质数了才进行操作,多余的操作会导致一个p[i]被轮换了多次最后回到i
if(A[i] == i) {
int m = g[i].size();
if (m > 1) {
int t = g[i][0];
for (int j = 0; j < m - 1; ++j) {
a[g[i][j]] = a[g[i][j + 1]];
}
a[g[i][m - 1]] = t;
}
}
}
for (int i = 1; i <= n; ++i) cout<<a[i]<<" ";
cout<<endl;
}
int main() {
int tt; cin>>tt;
while (tt--) solve();
return 0;
}

浙公网安备 33010602011771号