题意
给出一个长度为nnn的序列,初始序列为1,2,3,…,n1,2,3,\dots,n1,2,3,…,n,每次计算出当前序列的GCDGCDGCD并输出,然后可以任意删除序列中的一个数,重复前面操作,直到整个序列为空。要求输出的GCDGCDGCD序列字典序最大。
乱搞题解
很明显最后的GCD序列长度是nnn,通过手算几个样例我发现了当n>3n > 3n>3时一个规律,输出的GCD都是2i2^i2i,每4个2i2^i2i出现的次数是cnt=n−2i2i+1+1cnt = \frac{n-2^i}{2^{i+1}}+1cnt=2i+1n−2i+1,当i==0i == 0i==0且nnn为奇数时,cnt+1cnt+1cnt+1其余的时候可以当做n−1n-1n−1处理 。但这个需要特殊处理一下最后一次的GCD,最后的数字是n/last(2i)∗last(2i)n/last(2^i)*last(2^i)n/last(2i)∗last(2i),last(2i)last(2^i)last(2i)代表的是在n−1n-1n−1处出现的2i2^i2i。
正规题解
可以发现一开始需要删除掉全是奇数的数字cntcntcnt个,输出cntcntcnt个1,之后剩下的都是2,4,6,8,…2,4,6,8,\dots2,4,6,8,…的序列,这相当于1,2,3,4,…1,2,3,4,\dots1,2,3,4,…乘一个2,所以问题还是和前面一样,删除下标为奇数的数字cntcntcnt个,然后输出最前面的那个数字cntcntcnt个。
然后不断将问题缩小,当n≤3n \leq 3n≤3时要特殊处理一下。
乱搞代码
#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b) {
return b == 0 ? a: gcd(b,a%b);
}
int main() {
int n;
scanf("%d", &n);
int cnt = 0;
if(n <= 3) {
if(n == 3)
cout <<"1 1 3" << endl;
if(n == 2)
cout << "1 2" << endl;
if(n == 1)
cout << 1 << endl;
return 0;
}
int last;
int sum = 0;
int p = 1;
sum++;
cout << p << " ";
last = p;
int f = 2;
int t = n;
while((n-p)/f > 0) {
int cnt = (n-p)/f;
if(n&1) n--;
for(int i = 0; i < cnt; ++i)
cout << p <<" ";
sum += cnt;
last = p;
p *= 2; f *= 2;
if(sum < t-1) {
sum++;
cout << p <<" ";
last = p;
}
}
cout << n/last*last << endl;
return 0;
}
正规代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 6;
int seq[maxn];
int ans[maxn];
int ptr = 0;
void solve(int n, int mul){
if(n == 1){ans[ptr++] = mul; return;}
if(n == 2){ans[ptr++] = mul; ans[ptr++] = mul * 2; return;}
if(n == 3){ans[ptr++] = mul; ans[ptr++] = mul; ans[ptr++] = mul * 3; return;}
for(int i = 0; i < n; i++)if(seq[i]&1)ans[ptr++] = mul;
for(int i = 0; i < n/2; i++)seq[i] = seq[2*i + 1]/2;
solve(n/2, mul * 2);
}
int main(){
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)seq[i] = i + 1;
solve(n, 1);
for(int i = 0; i < n; i++)printf("%d ", ans[i]);
return 0;
}