题面
【问题描述】
MrKill上了高数之后超无聊,觉得好像很简单的样子。
一天老师看他很不爽,下课约到了办公室(很抱歉现实中大学咩有老师会约你)。一张无限长的纸上,写了n个数,分别是1到n。老师觉得好像他全懂了,指着这张纸:“你知道GCD吗?我今天上课才讲的。”
“这还不简单???”
“那把这些n个数的GCD写下来”
老师随机删了一个数又这么问,反复了n-1次之后,只有一个数了,老师推了推眼镜,把这个数填在了你写的
序列的末尾。MrKill获得了传说道具——LS序列。
老师重新抽了一张无限长的纸写了n个数,分别是1到n。老师把笔给了你,让你也来动手删一删,然后写下
字典序最大的LS序列就让你放学。MrKill手足无措,满头大汗,他还想着马上赶去聚会呢。
救救孩子。
【输入格式】
输入文件名为 sequence.in。
输入多个数表示n(小数据不超过50个、大数据不超过1个)读到0结束。
【输出格式】
输入文件名为 sequence.out。
输出数据为多串长度为n的序列,代表字典序最大的LS序列
【输入输出样例1】
sequence.in | sequence.out |
---|---|
3 | 1 1 3 |
2 | 1 2 |
1 | 1 |
0 |
样例1解释:
对于序列1、2、3
① 一个数没有删的时候(GCD(1,2,3)= 1)
② MrKill选择把2先删了发现没有什么用(GCD(1,3)= 1)
③ MrKill发现删了1可以变大(GCD(3)= 3)
他不知道怎么回事就做对了
【数据规模与约定】 (这道题我真的不知道怎么送分)
对于20%的数据(n ≤ 10)
对于60%的数据 (n ≤ 1000)
对于 100%的数据 (n ≤ 5*10^6)
题解
一道偏简单的性质构造题
我们考虑这样一个事实(GCD ( a,a+1)= 1),为了让字典序最大,我们需要尽快把相邻的两项清掉,但是删去
奇数还是偶数呢?当然是删奇数,因为偶数有共同的(GCD=2),奇数却不一定有共同的(GCD)。这样我们就保
证了最快使得非1元素出现,贪心使得字典序最大。
接下来怎么办呢?
我们把剩下的数除以2,再做一遍刚才一样的操作,利用一个计数器,记录除了多少个二或者直接记录(2^k) 这样,
一步一步除下来,就可以得到最终序列了。
然而这样处理到最后,需要判断(n=1,2,3)的情况,留给你们自己思考(考场上应该就做出来的)。
可能还有一些小朋友没理解,可以去和别人讨论讨论,这是一种相当典型的第一题用去奇数再除以2的序列递
归。除了序列递归,还有很多种可以利用除以2来解决的事情,可以考虑考虑再出一道题坑别人,防止被坑。
p.s:我们并不是直接对序列进行除以2的操作,而是知道了序列长度,我就能够通过除以2来知道当前GCD应
该要删掉多少个数之后才能变大,而变大之前,这个数是定值。
后话
样例其实很良心了,把需要特殊处理的1,2,3的情况都给出来了
(n=2)的情况,可以显然得出前一个数和后一个成1:2的关系
(n=3)的情况,例如(2,4,6)这个例子,我们先删除2,再删除4,然后删除6,可以显然得出是字典序最大的方案
也就是(2,2,6)即(now,now,now*3)
那么我们每次把数列缩小到二分之一,递归处理,注意特判(n=1,n=2,n=3)的情况即可
代码
#include <bits/stdc++.h>
using namespace std;
void solve(int n, int now) {
if (!n) {
puts("");
return;
}
if (n == 1) {
printf("%d
", now);
return;
}
if (n == 2) {
printf("%d %d
", now, now * 2);
return;
}
if (n == 3) {
printf("%d %d %d
", now, now, 3 * now);
return;
}
for (int i = 1; i <= (n + 1) / 2; i++) printf("%d ", now);
solve(n >> 1, now * 2);
}
int main() {
while (1) {
int n;
scanf("%d", &n);
if (!n)
break;
solve(n, 1);
}
return 0;
}