CF1605E Array Equalizer

发布时间 2023-12-07 11:57:38作者: Hanx16Msgr

Array Equalizer

题面描述

Jeevan 有两个长度为 \(n\) 的数组:\(a\)\(b\)。他有以下两种操作:

  • 选择一个 \(k\)\(1 \le k \le n\)),对所有满足 \(1 \leq i \leq n\) 并且 \(1 \le i \times k \le n\)\(i\),令 \(a_{ik}=a_{ik} + 1\)
  • 选择一个 \(k\)\(1 \le k \le n\)),对所有满足 \(1 \leq i \leq n\) 并且 \(1 \le i \times k \le n\)\(i\),令 \(a_{ik}=a_{ik} - 1\)

不幸的是,他忘记了 \(b_1\),因此他会向你提问 \(q\) 次,每次给出一个 \(x\),表示:

  • 如果 \(b_1 = x\),那么把 \(a\) 变为 \(b\) 至少需要几次操作?

\(1\le n\le 2\cdot 10^5,1\le a_i,b_i\le 10^6, b_1=-1,1\le q\le2\cdot 10^5,1\le x_i\le 10^6\)

Solution

题目没有要求判断无解,那么先想如何构造一种通解。考虑枚举每次修改的起始位置(即每次修改的 \(k\)),容易发现调换每次修改的顺序不会使得答案发生变化,所以不妨从小到大枚举 \(k\)。如果当前修改的是 \(k\),那么前 \(k-1\) 个数一定已经修改完成了,否则之后的修改都无法操作到前面这 \(k-1\) 个数。因此正确的贪心方法就是从小到大依次使得每个 \(a_i=b_i\),直接暴力模拟的话就是 \(\mathcal O(qn\log n)\)

\(+1\) 操作的贡献为 \(1\)\(-1\) 操作的贡献为 \(-1\),那么设 \(f_i\) 表示 \(a_i\) 修改到 \(b_i\) 所用的修改次数,设 \(c_i=a_i-b_i\),有:

\[f_i=c_i-\sum\limits_{d\mid i\land d\neq i}f_d \]

因为每次修改影响的是 \(c_1\),因此考虑将 \(f\)\(c\) 表示。

移项得:

\[c_i=\sum\limits_{d\mid i}f_d \]

用迪利克雷卷积来表示就是 \(c=f*I\)。那么对这个式子使用莫比乌斯反演得到 \(f=c*\mu\),即:

\[f_i=\sum\limits_{d\mid i}c_i\cdot \mu(\dfrac{i}{d}) \]

\(c_1\) 项提出,有:

\[f_i=\left(\sum\limits_{d\mid i\land d\neq 1}c_i\cdot\mu(\dfrac i d)\right)-c_1\cdot\mu(i) \]

答案显然是 \(\displaystyle\sum\limits_{i=1}^n|f_i|\)

由于存在绝对值,因此考虑讨论 \(f_i\) 的正负。令 \(v_i=\displaystyle\sum\limits_{d\mid i\land d\neq 1}c_i\cdot\mu(\dfrac i d)\),显然 \(v_i\) 可以 \(\mathcal O(n\log n)\) 预处理得出。那么按照 \(\mu(i)\) 的三种取值进行分讨:

  • \(\mu(i)=0\) 时,\(f_i\)\(c_1\) 无关,可以预处理直接算出。
  • \(\mu(i)=1\)\(\mu(i)=-1\) 时,将 \(v_i\) 排序,那么所有 \(f_i<0\)\(i\) 是一个前缀,\(f_i\ge 0\)\(i\) 是一个后缀,因此直接二分找到分界点即可。

总时间复杂度为 \(\mathcal O(n\log n+q\log n)\)

Code
// Cirno is not baka!
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = (a); i <= (int)(b); ++i)
#define Rof(i, a, b) for (int i = (a); i >= (int)(b); --i)
#define FILE(filename) { \
    freopen(#filename ".in", "r", stdin); \
    freopen(#filename ".out", "w", stdout); \
}
#define All(x) x.begin(), x.end()
#define rAll(x) x.rbegin(), x.rend()
#define pii pair<int, int>
#define fi first
#define se second
#define i64 long long
#define mkp make_pair
#define int long long
#define epb emplace_back
#define pb push_back
using namespace std;

const int _N = 2e5 + 5, mod = 1e9 + 7, inf = 1e9;
template<typename T> void Max(T &x, T y) {x = max(x, y);}
template<typename T> void Min(T &x, T y) {x = min(x, y);}

namespace BakaCirno {
    int N, M, A[_N], B[_N];
    bitset<_N> flag;
    vector<int> prim;
    int mu[_N];
    void InitPrime(int n) {
        mu[1] = 1;
        For(i, 2, n) {
            if (!flag[i]) {
                prim.epb(i);
                mu[i] = -1;
            }
            for (int j : prim) {
                if (i * j > n) break;
                flag[i * j] = 1;
                if (i % j == 0) {mu[i * j] = 0; break;}
                mu[i * j] = -mu[i];
            }
        }
    }
    int val[_N];
    int t1[_N], tot1, t2[_N], tot2, sum3;
    int pre1[_N], pre2[_N], suf1[_N], suf2[_N];
    void Init() {
        InitPrime(N);
        For(i, 2, N) for (int j = i; j <= N; j += i)
            val[j] += mu[j / i] * (A[i] - B[i]);
        For(i, 2, N) {
            if (mu[i] == 0) sum3 += abs(val[i]);
            if (mu[i] == 1) t1[++tot1] = val[i];
            if (mu[i] == -1) t2[++tot2] = val[i];
        }
        sort(t1 + 1, t1 + tot1 + 1), sort(t2 + 1, t2 + tot2 + 1);
        For(i, 1, tot1) pre1[i] = pre1[i - 1] + t1[i];
        For(i, 1, tot2) pre2[i] = pre2[i - 1] + t2[i];
        Rof(i, tot1, 1) suf1[i] = suf1[i + 1] + t1[i];
        Rof(i, tot2, 1) suf2[i] = suf2[i + 1] + t2[i];
    }
    int Solve(int x) {
        int res = sum3 + abs(A[1] - x), L, R;
        L = 1, R = tot1;
        while (L <= R) {
            int mid = (L + R) >> 1;
            if (t1[mid] + A[1] - x >= 0) R = mid - 1;
            else L = mid + 1;
        }
        res += (x - A[1]) * R - pre1[R] + suf1[L] + (tot1 - L + 1) * (A[1] - x);
        L = 1, R = tot2;
        while (L <= R) {
            int mid = (L + R) >> 1;
            if (t2[mid] - A[1] + x >= 0) R = mid - 1;
            else L = mid + 1;
        }
        res += (A[1] - x) * R - pre2[R] + suf2[L] - (tot2 - L + 1) * (A[1] - x);
        return res;
    }
    void _() {
        cin >> N;
        For(i, 1, N) cin >> A[i];
        For(i, 1, N) cin >> B[i];
        Init();
        cin >> M;
        For(i, 1, M) {
            int x; cin >> x;
            cout << Solve(x) << '\n';
        }
    }
}

signed main() {
    // FILE(test);
    cin.tie(0)->sync_with_stdio(0); int T = 1;
    // cin >> T;
    while (T--) BakaCirno::_();
    // fout.flush();
}