给出数列Ai~An,求
F(x)=∑i=1N(⌊Aix⌋+Aimodx)
的最小值。
教训:因为不需要使用long long的数组开成了long long,TLE了好多次。
做法:
预处理出f[n](小于等于n有多少个数)和s[n](小于等于n的数之和)。
暴力枚举x(从2到最大的一项),对于每个x,可以把这些数分成若干段,每段具有类似的性质。即Ai / x的值相同。
例如,数列为 2,4,5,6,8,9,13,14.........,当x=5,2,4可以分为1组,5,6可以分为一组,8,9为一组,13,14为一组
枚举Ai/x的值,就可以O(1)计算出这段中Ai/x+Ai%x的值。(tips:A%X=A-(A/X)×X)。设N=max(Ai),均摊的复杂度为O(NlogN)。
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
const int MAXM=1E6;
int a[MAXM+5],f[MAXM+5];
LL s[MAXM+5];
void work()
{
int i,j,k,t,m,n,maxa;
LL ans,sum,x,temp,lim;
maxa=0;
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&a[i]),maxa=max(maxa,a[i]);
for(i=0;i<=maxa;i++) s[i]=f[i]=0;
for(i=0;i<n;i++)
{
f[a[i]]++;
s[a[i]]+=(LL)a[i];
}
for(i=1;i<=maxa;i++)
{
f[i]+=f[i-1];
s[i]+=s[i-1];
}
ans=s[maxa];
for(k=2;k<=maxa;k++)
{
sum=0;
for(i=0;i*k<=maxa;i++)
{
int x=max(0,i*k-1);
int y=min(maxa,(i+1)*k-1);
sum+=(LL)i*(LL)(f[y]-f[x]);
sum+=s[y]-s[x]-(LL)(f[y]-f[x])*(LL)i*(LL)k;
if(sum>=ans) break;
}
ans=min(ans,sum);
}
printf("%lld\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--) work();
return 0;
}