题意:给出一个数列.在数列中选取一个数,然后求把数列中的数变成小于他们的并且是这个数的倍数的数,求变化之后的数列的和最大值。
思路: 将每个数的个数记录下来,用前缀和记录下来。然后枚举每一个数,以他们的倍数进行计算出选取这个数后数列和。
以他们的倍数计算是:
比如以枚举a[i] ,那么在 a[i] * j 和 a[i] * (j + 1) - 1这个区间的数经过变换之后都是a[i] * j ,所以我们只要知道这些数的个数就能计算了,个数的话就要用到刚刚计算出的前缀和了。
PS :为了避免超时,同一个a[i]枚举过就不要枚举,所以用一个vis数组标记一下/。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
typedef long long ll;
int a[maxn];
int cnt[maxn];
ll sum[maxn + 100];
int n;
bool vis[maxn];
int main()
{
while( ~ scanf("%d",&n))
{
memset(cnt,0,sizeof(cnt));
memset(vis,false,sizeof(vis));
int maxs = 0;
for(int i = 1; i <= n;i ++)
scanf("%d",&a[i]),cnt[a[i]] ++,maxs = max(maxs,a[i]);
sum[0] = 0;
for(int i = 1; i <= maxn; i ++)
sum[i] = sum[i - 1] + cnt[i];
ll ans = 0;
for(int i = 1; i <= n; i ++)
{
if(vis[a[i]])continue;vis[a[i]] = true;
ll temp = 0;
for(int j = 2; (j - 1)* a[i] <= maxs; j ++)
{
if(j * a[i] > maxs)
{
temp += (sum[maxs ] - sum[(j - 1) * a[i] - 1]) * a[i] * (j - 1);
}
else
{
temp += (sum[j * a[i] - 1] - sum[a[i]*(j - 1) - 1] )* a[i] * (j - 1);
}
}
ans = max(ans,temp);
}
printf("%I64d\n",ans);
}
return 0;
}