Codeforces 851 D. Arpa and a list of numbers

問題文
http://codeforces.com/contest/851/problem/D

問題文で与えられたa_iが数直線上にあることを考える

区間ごとに分けることを考えてcost(g)という関数を定義する。もし、全体の最大公約数がgとしたいなら、答えはg1より大きい時のcost(g)の最小値の和。

それぞれの区間でのcost(g)をもとめる、ある数(例えばc)が区間に含まれる時、かかるコストはそれをxで消すか、gで割れるまで足していった時なので

min(x,y\times( \lceil \frac{c}{g}\rceil)\times g-c)
になる。
次に \lceil \frac{c}{g}\rceil\times gの取る値について考える。

本題に入る前に二つの関数を定義する

-cnt(l,r) : l\leq a_i\leq rとなるa_iの個数を返す関数。この関数を計算するために配列bを定義する。b[x]0からxまでに含まれるa_iの個数。この配列を用いるとcnt(l,r)=b[r]-b[l-1]と書ける
-sum(l,r) : \sum_{i\ni l\leq a_i\leq r}^{}a_iを返す関数。この関数を計算するために配列psを定義する。ps[x]0からxまでに含まれるa_iの総和。この配列を用いるとsum(l,r)=ps[r]-ps_[l-1]とかける。

さて、それぞれのgの倍数kの時について(k-g,k]でのコストを計算して足し上げていく。
つまり、その範囲を二つの区間に分割する(k-g,k-f]と(k-f,k]に分割するようなもっともよいfを見つけ、一つ目の区間(図の青線部分)の数を削除し、二つ目の区間(図の赤線部分)の数をkになるまで足し上げていけばよい。
求めるfは もし、kとの差がx/yを超えるならxで消すほうがコストが安いので
f=min(g,x/y)
よってcost(g)=cnt(k-g+1,k-\lceil f\rceil)\times x + (cnt(k-\lceil f\rceil + 1,k)\times k - sum(k-\lceil f\rceil +1,k))\times y


時間計算量はO(\max(a_i)\log \max(a_i))
f:id:albicilla:20170908133457p:plain

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 2000000 + 10;
const long long INF = 1e18;

int n;
long long x, y;
long long b[N];
long long ps[N];

long long cnt(long long l,long long r){
  return b[r]-b[l-1];
}
long long sum(long long l,long long r){
  return ps[r]-ps[l-1];
}

void solve()
{
    cin >> n >> x >> y;
    for(int i = 0; i < n; ++ i) {
        int t;
        scanf("%d", &t);
        b[t] ++;
        ps[t] += t;
    }
    long long ans = INF;
    long long c = x / y;

    for(int i = 1; i < N; ++ i) {
        b[i] += b[i - 1];
        ps[i] += ps[i - 1];
    }

    for(long long g = 2; g <=1000000; g++) {
        long long tmp_ans = 0;
        long long f=min(g,(x+y-1)/y);
        for(long long k = g; k <= 1000000+g; k += g) {
          tmp_ans+=cnt(k-g+1,k-f)*x;
          tmp_ans+=(cnt(k-f+1,k)*k-sum(k-f+1,k))*y;
        }
        ans = min(ans, tmp_ans);
    }
    cout << ans << endl;

}

int main()
{
	solve();
	return 0;
}