CF1858C Yet Another Permutation Problem

题意

给出 $n$,请构造一个长度为 $n$ 的排列 $a$,使得一个数组 $d (d_i = \gcd(ai,a{i \mod n}))$ 中不同数字的个数最大,下文将该值称作价值

思路

先考虑升序的排列:$1,2,3,\dots,n - 1,n$,其价值显然为 $1$。

对于每个 $a_i$:

要使其作出不同贡献,$a_{i + 1}$ 应当为 $a_i$ 的倍数。

所以,可以先升序填数,判断 $a_i$ 最小的除自己外的倍数,即二倍数是否在 $n$ 的范围以内。如果在,将其排在 $a_i$ 之后,重复此操作直至无法继续。无法进行时,选择剩余能选择的数中最小的数排在 $a_i$ 之后。尽可能的使所有数都作出贡献,序列的价值才是最大的。

Code

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define db double
#define M 100010

#define For(i, j, k) for (int i = j; i <= k; i++)
#define Rep(i, j, k) for (int i = j; i >= k; i--)
#define mem(a, b) memset(a, b, sizeof a)
#define INF 0x3f3f3f3f
#define LNF 0x3f3f3f3f3f3f3f3f
#define endl '\n'
#define IOS (ios::sync_with_stdio(0), cin.tie(0), cout.tie(0))

int t;
int n;
int ans[M];
bool vis[M];

signed main(){
    IOS;
    cin >> t;
    while(t --){
        cin >> n;
        int num = 1;
        For(i,1,n) vis[i] = false;
        For(i,1,n){
            while(vis[num]) num ++;//如果已使用,就跳过
            ans[i] = num;//初始为最小的可选数
            vis[num] = true;
            if(i == 1) continue;//注意:如果对1执行操作,只能产生di = 1的贡献,而di = 1所有互质的数都能产生,故不对1进行操作
            int j = i;
            while(ans[j] * 2 <= n && j < n){//一直往后跳,寻找自己的倍数
                j ++;
                ans[j] = ans[j - 1] * 2;
                vis[ans[j]] = true;
            }
            num ++,i = j;
        }
        For(i,1,n) cout << ans[i] << ' ';
        cout << endl;
    }
    return 0;
}
posted @ 2023-08-16 02:14  固态H2O  阅读(4)  评论(0编辑  收藏  举报  来源