zhao_ry514114
赵若伊

导航

 

思路

考虑一次操作,假设最开始选择的数为 \(x\)。

那么这一轮对整个数组减去的数依次为 \(x,\lfloor\frac{x}{2}\rfloor,\lfloor\frac{x}{2^2}\rfloor,\cdots\)。

假设总共进行了 \(k\) 次操作,第 \(j\) 次选择的初始值为 \(x_j\)。

那么对于数组中的第 \(i\) 项,有 \(A_i=\sum_{j=1}^{k}\lfloor\frac{x_j}{2^{i-1}}\rfloor\)。

接下来考虑相邻的两项 \(A_{i-1}\) 和 \(A_i\)。

对于任意一个整数 \(x\),都有 \(\lfloor\frac{x}{2^{i-2}}\rfloor=2\lfloor\frac{x}{2^{i-1}}\rfloor\) 或 \(2\lfloor\frac{x}{2^{i-1}}\rfloor+1\)。

因此一次操作对 \(A_{i-1}-2A_i\) 的贡献只能是 \(0\) 或 \(1\)。

于是进行 \(k\) 次操作后,一定满足 \(0\le A_{i-1}-2A_i\le k\)。

所以首先必须有 \(A_{i-1}\ge 2A_i\)。

如果存在某个位置不满足这个条件,那么显然无解。

最少操作次数

如果满足上面的条件,对于每一个 \(i\),令 \(d_i=A_{i-1}-2A_i\)。

由于每次操作对 \(d_i\) 最多贡献 \(1\),所以至少需要 \(d_i\) 次操作。

因此答案至少为 \(\max d_i\)。

另一方面,可以把每个 \(d_i\) 理解为所有操作选择的 \(x\) 中,对应二进制位为 \(1\) 的数量。

不同二进制位可以独立决定,因此可以构造出这些 \(x\),使得每一个位置恰好有 \(d_i\) 个 \(1\)。

所以当数组不全为 \(0\) 时,最少操作次数就是 \(\max(1,\max_{i=2}^{n}(A_{i-1}-2A_i))\)。

这里还需要和 \(1\) 取最大值,因为只要数组中存在非零元素,就至少需要进行一次操作。

如果整个数组本来就全是 \(0\),答案显然为 \(0\)。

代码

#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = (j); i <= (k); i ++)
#define per(i,j,k) for (int i = (j); i >= (k); i --)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double
#define mkp make_pair
#define eb emplace_back
#define e_ emplace
#define ls (p << 1)
#define rs (p << 1 | 1)
//#define int ll
using namespace std;
const int N = 35;
//const int MOD = 998244353;
//const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const ll LINF = 4e18;
int T;
int n, mx;
int a[N];

signed main () {
    //freopen (".in", "r", stdin);
    //freopen (".out", "w", stdout);
    ios::sync_with_stdio (0);
    cin.tie (0);
    cin >> T;
    while (T --) {
        mx = 1;
        cin >> n;
        bool ck = true, em = true;
        rep (i, 1, n) {
            cin >> a[i];
            if (a[i] != 0) em = false;
            if (i != 1) {
                if (a[i - 1] < a[i] * 2) ck = false;
                mx = max (mx, a[i - 1] - a[i] * 2);
            }
        }
        if (!ck) cout << -1 << '\n';
        else if (em) cout << 0 << '\n';
        else cout << mx << '\n';
    }
    return 0;
}

复杂度

每组数据只需要遍历一次数组。

时间复杂度为 \(O(N)\)。

如何想到

一开始可以先考虑一次操作对相邻两个位置的影响。假设这一轮在前一个位置减去 \(x\),那么后一个位置减去的是 \(\lfloor\frac{x}{2}\rfloor\)。

由于向下取整的性质,\(x\) 相对于 \(\lfloor\frac{x}{2}\rfloor\) 只可能有两种情况:

\(x=2\lfloor\frac{x}{2}\rfloor\) 或 \(x=2\lfloor\frac{x}{2}\rfloor+1\)。

也就是说,一次操作中,前一个位置减去的数一定是后一个位置减去的数的 \(2\) 倍或者 \(2\) 倍加 \(1\)。所以如果观察 \(A_{i-1}-2A_i\),一次操作只能让这个差值减少 \(0\) 或 \(1\)。

这样问题就变成了不断把这些差值缩小到 \(0\)。因此首先要求 \(A_{i-1}-2A_i\ge0\),否则无论如何都不可能消掉;而一个差值每次最多减少 \(1\),所以至少需要 \(A_{i-1}-2A_i\) 次操作。

于是自然想到答案与所有 \(A_{i-1}-2A_i\) 的最大值有关,最终得到 \(\max(1,\max(A_{i-1}-2A_i))\)。

posted on 2026-09-11 19:11  zhao_ry514114  阅读(12)  评论(0)    收藏  举报