1. 题目来源
链接:3636. 数组延伸
2. 题目解析
数学找规律,一开始将题意理解错了…
当 a[i]
可以被 x
整除时,在数组后面添加 x
个 a[i]/x
,可以发现这些数都一样,且总和仍等于 a[i]
。
那么如果初始的 a
数组所有数都可以整除 x
,则会在数组后面依次填加 x
个 a[i]/x
,新添加的这一轮元素之和仍等于 a
的初始元素之和。
故这个问题就转化成了,求整除轮数,以及最后停下来的位置即可。
整除轮数就是数组中每个数能将 x
整除的次数最小值,那个最小值就是停下来的数。
每轮都贡献一个完整的 sum
,且最后停下来的位置之前的所有数也可以被整除,在此不包括停下来的位置的哪个数。
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),其中计算 x
的次数最坏情况是
l
o
g
n
logn
logn 的。
空间复杂度: O ( n ) O(n) O(n)
详细,但并不优雅:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n, x;
int a[N];
int main() {
int T;
scanf("%d", &T);
while (T -- ) {
scanf("%d%d", &n, &x);
LL sum = 0;
for (int i = 0; i < n; i ++ ) scanf("%d", a + i), sum += a[i];
LL cnt = 1e9, idx = 0;
for (int i = 0; i < n; i ++ ) {
int t = a[i], c = 0;
while (t % x == 0) c ++ , t /= x;
if (cnt > c) {
cnt = c, idx = i;
}
}
LL res = (cnt + 1) * sum;
for (int i = 0; i < idx; i ++ ) res += a[i];
printf("%lld\n", res);
}
return 0;
}
详细且优雅:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, x;
int w[N];
int main() {
int T;
scanf("%d", &T);
while (T -- ) {
scanf("%d%d", &n, &x);
for (int i = 0; i < n; i ++ ) scanf("%d", &w[i]);
LL sum = 0, psum = 0;
int cnt = N;
for (int i = 0; i < n; i ++ ) {
sum += w[i];
int c = 0;
for (int j = w[i]; j % x == 0; j /= x)
c ++ ;
if (c < cnt) {
cnt = c;
psum = sum - w[i];
}
}
printf("%lld\n", sum * (cnt + 1) + psum);
}
return 0;
}