Codeforces Round #376 (Div. 2) F. Video Cards 数学 & 暴力

http://codeforces.com/contest/731/problem/F

注意到一个事实,如果你要找一段区间中(从小到大的),有多少个数是能整除左端点L的,就是[L, R]这样。那么,很明显,把这个区间分段。分成[L, 2 * L - 1],因为这段区间中,都不大于等于L的两倍,这样就使得这段区间的贡献就是sum_number * L了。

就是中间有多少个数,每个数贡献一个L值。然后到枚举[2 * L, 3 * L - 1]这段区间,贡献的值就是sum_number * (2 * L)了。
因为2 * L是肯定能% L == 0的,所以加起来所有区间的贡献,就是以L作为起点的贡献。

所以就是预处理一下前缀和就OK了。book[i]表示<= i这个元素有多少个。

这样的复杂度是maxn + maxn / 2 + maxn / 3 + ....的,就是nlogn

当然还是有些边界要处理的。因为数值最大是200000,所以要预处理到2  * maxn

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 200000 + 20;
LL book[2 * maxn];
bool has[2 * maxn];
void work() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        int a;
        scanf("%d", &a);
        book[a]++;
        has[a] = true;
    }
    for (int i = 1; i <= 2 * maxn - 20; ++i) {
        book[i] += book[i - 1];
    }
    LL ans = -inf;
    for (int i = 1; i <= maxn - 20; ++i) {
        if (!has[i]) continue;
        LL t = 0;
        for (int j = i; j <= maxn - 20; j += i) {
            t += (book[j + i - 1] - book[j - 1]) * j;
        }
        ans = max(ans, t);
    }
    cout << ans << endl;
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    work();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5968222.html