CF C. Sequence Transformation【規律】
題意:給你一個n 然後代表1到n的一個序列
然後說每次進行一種操作 序列當前所有數的gcd 然後把gcd結果加入一個答案陣列 然後此時可以刪除序列中的一個數
重複這種操作 直至序列沒有數 同時保證答案序列是字典序最大 前面的數越大越好
收穫:樣例一般都會 給出具有代表性 而且比較特殊的 我們應該善於發現
首先1 2 3 4 5 6 7 8 。。。n
很明顯 這些數的gcd就是1
怎麼刪除數:可以發現這n個數中 1的倍數個數 >2的倍數個數 >3的倍數個數>4的倍數個數
所以想到了 把2的倍數保留下來 其餘的刪走 就是有刪走個數 個1
同理 再把4的倍數保留下來 其餘的刪走 就是有刪走個數 個2
同理 再把8的倍數保留下來 其餘的刪走 就是有刪走個數 個4
同理 再把16的倍數保留下來 其餘的刪走 就是有刪走個數 個8
同理 再把32的倍數保留下來 其餘的刪走 就是有刪走個數 個16
。。。。直到沒數
畫了一下可以發現 每次刪除的個數就是 即n - n/2 向下取整
刪完每次剩餘的是 沒刪除之前的n 除以 2 個
1 2 3 4 5 5個
2 4 2個
4 1個
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16個
2 4 6 8 10 12 14 16 8個
4 8 12 16 4個
8 16 2個
16 1個
現在知道了 答案陣列 加入的數(1 2 4 8 16。。。。)
又知道了每次刪除應該 加多少個上面的數 n - n/2個
#include<iostream>
using namespace std;
const int maxn = 1e6 + 100;
int ans[maxn];
int lc;
int main(){
int n;
while (cin >> n){
// if (n == 3){
// cout << "1 1 3" << endl;
// continue;
// }
lc = 0;
//要加的數
for (int add = 1;n;add*=2){
//看樣例 3 輸出 1 1 3 猜的 因為1 2 3 最優不是為了儲存2倍數 而刪除 1 和 3 而是刪除1 和 2
if (n == 3){
ans[lc++] = add, ans[lc++] = add, ans[lc++] = add * 3;
break;
}
int many = n - n/2;//刪除的個數 加上刪除個數 個 add
for (int j = 0; j < many; ++j){
ans[lc++] = add;
}
n = n / 2;//更新序列
}
for (int i = 0; i < lc;++i){
cout << ans[i] << " ";
}
cout << 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;
}