题目链接: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); } }