CF1605E Array Equalizer
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\),有:
因为每次修改影响的是 \(c_1\),因此考虑将 \(f\) 用 \(c\) 表示。
移项得:
用迪利克雷卷积来表示就是 \(c=f*I\)。那么对这个式子使用莫比乌斯反演得到 \(f=c*\mu\),即:
将 \(c_1\) 项提出,有:
答案显然是 \(\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();
}