hdu 4961 Boring Sum【构造题】

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值