2024-2025 ICPC, NERC, Southern and Volga Russian Regional Contest_B. Make It Equal

//首先需要注意到,如果我们对当前序列中每个数都进行一次操作,那么实际上是令整个序列每个数都减一
//于是假设序列k,k,...,k是可以由当前序列转化过去的,那么k-1,k-1,...,k-1也是可以得到的,乃至小于等于k的每个数都是可以作为最终答案的
//于是我们发现答案具有单调性,即有答案的情况下,k越小答案越大,所以可以直接二分这个k值
//在check函数中,注意进行一轮操作可能并不能使得所有的数都小于等于k,需要一直操作,但实际上操作的次数不会很多,因为一轮操作后只会剩下一个数大于k
#include<bits/stdc++.h>
    
using namespace std;
    
long long t;
long long n,ans;
vector<long long> a;

bool check(long long k) {
    vector<long long> b(a);
    ans = 0;
    long long sum = 0;
    for(long long i = 1;i <= n;i++) sum += b[i];
    while(sum > k * n) {
        for(long long i = 1;i <= n;i++) {
            if(b[i] < k) {
                long long pos = i - 1;
                if(pos <= 0) pos = n;
                b[pos] -= (k - b[i]) * 2;
                ans += (k - b[i]);
                sum -= (k - b[i]);
                b[i] = k;
            }
            else if(b[i] > k) {
                long long tmp = b[i] - k;
                tmp = ceil(tmp * 1.0 / 2);
                b[i] -= tmp * 2;
                long long pos = i + 1;
                if(pos > n) pos = 1;
                b[pos] += tmp;
                ans += tmp;
                sum -= tmp;
            }
        }
    }
    for(long long i = 1;i <= n;i++)
        if((b[i] != b[i - 1] && i > 1) || b[i] < 0) return false;
    return true;
}

void solve() {
    cin >> n;
    a.assign(n + 10,0);
    for(long long i = 1;i <= n;i++) cin >> a[i];
    long long l = 0,r = 1e9;
    while(l < r) {
        long long mid = (l + r) >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
        if(r - l == 1) {
            if(check(r)) l = r;
            break;
        }
    }
    if(check(l)) cout << ans << '\n';
    else cout << -1 << '\n';
}
    
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> t;
    while(t--) solve();
    
    return 0;
}
posted @ 2025-04-10 12:44  孤枕  阅读(8)  评论(0)    收藏  举报