CF2157

超级失败的一场~

CF2157D Billion Players Game

首先,所有的点都得往中间指。因为如果一个右边的点向右指,左边的点向左指的话,那只要 \(p\) 取在这两个中间就一定会让代价减少。反之,如果让两个点配对向中间指,不论 \(p\) 在何处,贡献一定是线段长度。
\(a_i < l\) 的点肯定往右指,\(a_i > r\) 的点肯定向左指。那么基础答案就是 \(\sum_{a_i < l} l - a_i + \sum_{a_i > r} r - a_i\)
然后对于 \(a_i\) 在中间的点,仍然会产生贡献。同时,我们还要考虑现在有 \(b\)\(a_i < l\) 的点,\(c\)\(a_i > r\) 的点,那么就相当于 \(b\) 个点在 \(l\) 处,\(c\) 个点在 \(r\) 处。然后两两配对,最大化绝对值之和。排序后小配大即可。
贪心策略错时不妨拍一拍,让自己清楚地意识到假在哪,能不能修。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll a[N], n, l, r, p[N], tp;
void solve(){
    cin >> n >> l >> r;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    tp = 0;
    sort(a + 1, a + 1 + n);
    ll ans = 0;
    int x = 1, y = n;
    while(x <= n && a[x] < l) p[++tp] = l, ans += l - a[x], ++x;
    while(y >= 1 && a[y] > r) p[++tp] = r, ans += a[y] - r, --y;
    for(int i = x; i <= y; ++i) p[++tp] = a[i];
    sort(p + 1, p + 1 + n);
    for(int i = 1; 2 * i <= n; ++i) ans += p[n - i + 1] - p[i];
    cout << ans << '\n';
}
int main(){
    cin.tie(0)->sync_with_stdio(0);
    int T; cin >> T;
    while(T--) solve();
    return 0;
}

CF2157E Adjusting Drones

Haybale Guessing 同款的并查集。
首先二分答案,变成判断 \(t\) 次之后是否合法。我们按值域考虑肯定没问题。假设 \(a_i\)\(b_i\) 个。按 \(a_i\) 从大到小处理,我们发现只要后面比 \(a_i\) 大的那些数不在 \(a_i\) adjust 的时候跟它撞上,那么每次 \(a_i\) 自增之后的数量都会减 1。对于那些撞上的位置,发现上面一定只有一个无人机,因此数量不增不减。那么就变成染色问题了,跳到的空的个数就是可以减少的个数。验证最后减少的次数能不能让 \(b_i < k\) 即可。
复杂度 \(O(N \log N)\)

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 10;
int a[N], b[N], n, V, k, cnt[N], f[N];
void init(){ for(int i = 1; i <= 3 * V + 5; ++i) f[i] = i; }
int getf(int p){ return (p == f[p] ? p : f[p] = getf(f[p]));}
bool check(int t){
    init();
    for(int i = 1; i <= n; ++i) b[i] = cnt[a[i]];
    for(int i = n; i >= 1; --i){
        f[a[i]] = a[i] + 1;
        int p = getf(a[i]);
        for(; p <= a[i] + t; p = getf(p)){
            if(b[i] == 1) break;
            b[i]--;
            f[p] = p + 1;
        }
        if(b[i] > k) return 0;
    }
    return 1;
}
void solve(){
    cin >> n >> k;
    int l = 0, r = n; V = n;
    for(int i = 1; i <= 2 * n; ++i) cnt[i] = 0;
    for(int i = 1; i <= n; ++i) cin >> a[i], cnt[a[i]]++;
    sort(a + 1, a + 1 + n);
    n = unique(a + 1, a + 1 + n) - a - 1;
    while(l < r){
        int mid = (l + r) >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << '\n';
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    int T; cin >> T;
    while(T--) solve();
    return 0;
}

CF2157F Git Gud

一个策略是,我们先把所有数调成整除 2,然后再调成整除 4......
注意这里的整除指的是与 \(n\) 的差。
具体来说就是先 \(n - 1, n - 3, n - 5, \cdots\),每个加的值都是 1。这样都变成整除 2 了。然后再 \(n - 2, n - 6, n - 12, \cdots\),每个加的值都是 2。这样都变成整除 4 了。以此类推。
这样每层的代价是 \(1000 + O(n)\),因为两层衔接的话需要一次递增。那么总共就是 \(1000O(\log n) + O(n \log n)\)
还是不够优。
实际上,如果我们选 \(m\) 作为模数,先调成整除 \(m\),然后是 \(m^2\),这样做下去。对于第一层的构造方案就是

\[\begin{matrix} n - (m - 1) & n - (m - 1) - m & n - (m - 1) - 2m & \cdots \\ n - (m - 2) & n - (m - 2) - m & n - (m - 2) - 2m & \cdots \\ \vdots & \vdots & \vdots & \ddots \\ n - 1 & n - 1 - m & n - 1 - 2m & \cdots \end{matrix} \]

每个都加 1,这样就用 \(1000(m - 1) + n\) 的代价。现在所有数都整除 \(m\) 了。
对于第二层的情况,也是类似的,就是每个 \(+m\)
这样代价总和就是 \(\log_mn(1000(m - 1) + n)\),取大概 \(m = n^{\frac{1}{3}}\) 可以通过。

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2.5e5 + 5;
int a[N], b[N], tp, n, m, fl = 0;
void solve(int k){
    int y = pow(m, k), x = y / m;
    if(x > n) return;
    for(int i = 1; i < m; ++i){
        for(int j = n - y + i * x; j >= 1; j -= y){
            a[++tp] = j, b[tp] = x;
        }
    }
    solve(k + 1);
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(0);
    cin >> n;
    m = ceil(pow(n, 1. / 3.));
    solve(1);
    cout << tp << '\n';
    for(int i = 1; i <= tp; ++i) cout << a[i] << ' ' << b[i] << '\n';
    return 0;
}

posted @ 2025-11-27 16:22  Hengsber  阅读(9)  评论(0)    收藏  举报