这是一个很菜的 Oier 的博客|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2023-12-07 11:56阅读: 7评论: 0推荐: 0

CF1605E Array Equalizer

Array Equalizer

题面描述

Jeevan 有两个长度为 n 的数组:ab。他有以下两种操作:

  • 选择一个 k1kn),对所有满足 1in 并且 1i×kni,令 aik=aik+1
  • 选择一个 k1kn),对所有满足 1in 并且 1i×kni,令 aik=aik1

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

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

1n2105,1ai,bi106,b1=1,1q2105,1xi106

Solution

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

+1 操作的贡献为 11 操作的贡献为 1,那么设 fi 表示 ai 修改到 bi 所用的修改次数,设 ci=aibi,有:

fi=cididifd

因为每次修改影响的是 c1,因此考虑将 fc 表示。

移项得:

ci=difd

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

fi=diciμ(id)

c1 项提出,有:

fi=(did1ciμ(id))c1μ(i)

答案显然是 i=1n|fi|

由于存在绝对值,因此考虑讨论 fi 的正负。令 vi=did1ciμ(id),显然 vi 可以 O(nlogn) 预处理得出。那么按照 μ(i) 的三种取值进行分讨:

  • μ(i)=0 时,fic1 无关,可以预处理直接算出。
  • μ(i)=1μ(i)=1 时,将 vi 排序,那么所有 fi<0i 是一个前缀,fi0i 是一个后缀,因此直接二分找到分界点即可。

总时间复杂度为 O(nlogn+qlogn)

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();
}
posted @   Hanx16Msgr  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起