题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4961
题目大意:
给出一个数组a,数组b中,b[i]代表在a[i]之前的离a[i]最近的 a[i]的倍数,如果没有倍数,那么b[i]=a[i]。数组c中,c[i]代表在a[i]之后的离a[i]最近的 a[i]的倍数,如果没有倍数,那么c[i]=a[i]。
最后求Σb[i]*c[i]
我们可以动态地维护一个vis1和vis2数组。
vis1[i]代表从前往后遍历过程数组a中,在之前遍历过的数中,i最近出现在第几位,用来求b[i]。
vis2[i]代表从后往前遍历过程数组a中,在之前遍历过的数中,i最近出现在第几位,用来求c[i]。
遍历两次数组a,分别求出数组b,数组c。
每次遍历数组a时,当前数为a[i],利用vis数组求出a[i]的所有倍数中,离a[i]最近的一个倍数;如果没有找到,那么b[i]=a[i]或者c[i]=a[i]。
一边看代码,一边看思路会比较容易理解:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const long long NN=111111;
long long vis1[NN];
long long vis2[NN];
long long num[NN];
long long f[NN];
long long g[NN];
int main()
{
#ifndef ONLINE_JUDGE
freopen("D:/in.txt","r",stdin);
#endif
long long n;
while(~scanf("%I64d",&n)){
if(n==0)
return 0;
memset(vis1,-1,sizeof(vis1));
memset(vis2,-1,sizeof(vis2));
for(long long i=1;i<=n;i++){
scanf("%I64d",&num[i]);
}
long long maxn=0;
maxn=num[1];
f[1]=1;
for(long long i=2;i<=n;i++){
maxn=max(maxn,num[i-1]);
vis1[num[i-1]]=i-1;
long long minn=-1;
for(long long j=num[i];j<=maxn;j+=num[i]){
if(vis1[j]!=-1){
minn=max(minn,vis1[j]);//往后遍历应取最大
}
}
if(minn==-1){
f[i]=i;
}else{
f[i]=minn;
}
}
maxn=num[n];
g[n]=n;
for(long long i=n-1;i>=1;i--){
maxn=max(maxn,num[i+1]);
vis2[num[i+1]]=i+1;
long long minn=1<<29;
for(long long j=num[i];j<=maxn;j+=num[i]){
if(vis2[j]!=-1){
minn=min(minn,vis2[j]);//往前遍历应取最小
}
}
if(minn==(1<<29)){
g[i]=i;
}else{
g[i]=minn;
}
}
long long ans=0;
for(long long i=1;i<=n;i++){
ans+=num[f[i]]*num[g[i]];
}
printf("%I64d\n",ans);
}
}