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;
}