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;
}
posted @ 2025-12-19 15:54  Wuyou2008  阅读(2)  评论(0)    收藏  举报