序列重排
序列重排
给定一个长度为 $n$ 的整数序列 $a_{1},a_{2}, \dots ,a_{n}$。
请你对序列进行重新排序(也可以保持原序列),要求新序列满足每个元素(第 $1$ 个除外)都恰好是前一个元素的两倍或前一个元素的三分之一。
保证输入一定有解。
输入格式
第一行包含整数 $n$。
第二行包含 $n$ 个整数 $a_{1},a_{2}, \dots ,a_{n}$。
输出格式
一行 $n$ 个整数,表示排序后的序列。输出任意合法方案即可。
数据范围
前三个测试点满足 $2 \leq n \leq 10$。
所有测试点满足 $2 \leq n \leq 100$,$1 \leq a_{i} \leq 3 \times {10}^{18}$。
输入样例1:
6 4 8 6 3 12 9
输出样例1:
9 3 6 12 4 8
输入样例2:
4 42 28 84 126
输出样例2:
126 42 84 28
输入样例3:
2 1000000000000000000 3000000000000000000
输出样例3:
3000000000000000000 1000000000000000000
解题思路
首先这题保证有解。假设有满足题目要求的序列$a_{1}~~a_{2}~\dots~a_{n}$。用$x_{i}$来表示第$i$个数的因子$2$的数量,用$y_{i}$来表示第$i$个数的因子$3$的数量。
在这个序列中除了第$1$个数外,对于第$i$个数,要么$a_{i}$是$a_{i-1}$的两倍,即$x_{i}$比$x_{i-1}$多$1$;要么$a_{i}$是$a_{i-1}$的$\frac{1}{3}$,即$y_{i}$比$y_{i-1}$少$1$。因此可以发现,对于任意一个数,它要么比前一个数的因子$2$多$1$,要么与前一个数的因子$2$数量相同。因此有$x_{i} \leq x_{i+1}$,即数列$x_{i}$是递增的。
如果某一个数与它前一个数的因子$2$的数量相同,即$x_{i} = x_{i-1}$,由于这是一个合法的序列,因此必然有这个数的因子$3$数量比前一个数的少$1$,即$y_{i} = y_{i-1} - 1$。
因此我们可以得到一个必要条件,就是如果这是一个合法序列,那么必然有($x_{i} < x_{i+1}$)或者($x_{i} = x_{i+1}$且$y_{i} > y_{i+1}$)。这是一个必要条件,也就是说如果是一个合法序列那么这个序列一定满足这个必要条件。而如果某个序列满足必要条件,那么这个序列不一定是一个合法序列。
由于保证有解,因此对于任何一个序列重新排完序后,满足这个必要条件的序列是唯一的。下面来证明。
首先不会有任何两个数$a_{i}$,$a_{j}$的因子$2$数量和因子$3$的数量相同,即$x_{i} = x_{j},~ y_{i} = y_{j}$。反证法,假设存在这个情况的话,首先$i,~ j$必然会是这个合法序列中的某两个位置,假设这两个数之间的数有$a_{i}~~a_{k_{1}}~~a_{k_{2}}~\dots~a_{j}$,因为这是一个合法序列,因此有$x_{i} \leq x_{k_{1}} \leq x_{k_{2}} \leq \dots \leq x_{j}$,由于$x_{i} = x_{j}$,因此中间所有数的因子$2$数量相等,即$x_{i} = x_{k_{1}} = x_{k_{2}} = \dots = x_{j}$。因为是合法序列,因子$2$的数量都相等,因此必然会有$y_{i} > y_{k_{1}} > y_{k_{2}} > \dots > y_{j}$,这就与$y_{i} = y_{j}$矛盾了。因此结论是如果要使序列可以变成合法序列,那么一定不会存在两个数的因子$2$和因子$3$的数量分别相等。因此序列中任何两个数的$x_{i}$和$y_{i}$不相等,根据双关键字排序,由于任何两个数都不会相等(即同时满足$x_{i} = x_{j},~ y_{i} = y_{j}$),因此每个数都有严格的大小顺序,因此他们排完序后的结果是唯一的。
所有,由于序列是有解的,因此不存在有两个数的因子$2$和因子$3$数量都分别相等,因此可以构造出一个满足必要条件的合法的序列,并且满足这个必要条件的序列是唯一的。
假设合法的序列是$S$。我们根据双关键字排序后得到一个序列$S_{1}$,这个序列满足必要条件,但不一定是合法的序列。又因为满足必要条件的序列是唯一的,因此$S_{1}$就是$S$。这是因为$S$满足必要条件,$S_{1}$也满足必要条件,因为一个序列满足必要条件不一定满足充分条件,但因为满足必要条件的序列是唯一的,因此满足必要条件的序列也会满足充分条件。
AC代码如下:
1 #include <cstdio> 2 #include <vector> 3 #include <algorithm> 4 using namespace std; 5 6 typedef long long LL; 7 8 const int N = 110; 9 10 vector<LL> a[N]; 11 12 int get(LL n, int b) { 13 int ret = 0; 14 while (n % b == 0) { 15 ret++; 16 n /= b; 17 } 18 return ret; 19 } 20 21 int main() { 22 int n; 23 scanf("%d", &n); 24 for (int i = 0; i < n; i++) { 25 LL val; 26 scanf("%lld", &val); 27 a[i] = {get(val, 2), -get(val, 3), val}; // 因为第二关键字要升序,因此乘个负号 28 } 29 30 sort(a, a + n); 31 32 for (int i = 0; i < n; i++) { 33 printf("%lld ", a[i][2]); 34 } 35 36 return 0; 37 }
参考资料
AcWing 4211. 序列重排(AcWing杯 - 周赛):https://www.acwing.com/video/3668/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16088480.html