codeforces 851d

昨晚的一道CF题,orz没想到这样的前缀和

传送门:Arpa and a list of numbers

题意:
给一个序列,有两种操作
1、删除一个数,需要花费x
2、将某个数的值增加1,需要花费y

现在要将这个序列变成空或者序列里的值gcd不等于1。
问至少要花费多少


思路:
做两个前缀和,一个用来记录有多少个数,一个用来记录到这个数的时候,和是多少。
那么我们当x >= a * y,a次增加操作的时候,使用2操作将它边到某个值,其他时候都使用1操作直接删除。

然后枚举每一个因子,因为数就只有 106 ,我们可以很好的用筛法的原理,在接近O(n)的时间复杂度下,枚举每一个数以及他们的倍数。
对于每一个倍数j到这个倍数减去因数i的这个区间里,可以做2操作的数,我们可以直接通过前缀和得到有多少个数,且他们的数的和是多少,再减去他们原本值的和,乘上y,就是将这些数变成j的花费。剩下的数都进行1操作,把他们都删掉,乘上x即可。

这里要特别注意的是。

题目给的是 106 但是他的可到倍数可能不止 106 ,可以枚举多一倍
#include <bits/stdc++.h>

#define MAXN 2000005
#define ll long long

using namespace std;

ll s1[MAXN];
ll s2[MAXN];

int main() {
    ll n, x, y, a;
    scanf("%lld %lld %lld", &n, &x, &y);
    ll p = (ll)(x * 1.0 / y) + 1;
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", &a);
        s1[a]++;
        s2[a] += a;
    }
    for (ll i = 1; i < MAXN; i++) {
        s1[i] += s1[i - 1];
        s2[i] += s2[i - 1];
    }
    ll ans = MAXN * n * y;
    for (ll i = 2; i < MAXN; i++) {
        ll tot = 0;
        for (ll j = i; j < MAXN && tot < ans; j += i) {
            ll k = max(j - i, j - p);
            tot += ((s1[j] - s1[k]) * j - (s2[j] - s2[k])) * y + (s1[k] - s1[j - i]) * x;
        }
        ans = min(tot, ans);
    }
    printf("%lld\n", ans);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值