HDU 5288 OO’s Sequence (二分)2015多校训练一

来源:互联网 发布:百度数据库在哪里 编辑:程序博客网 时间:2024/05/26 22:58

本题可以转化为对于每个数,求它向左向右分别能延伸多远直到遇到它的第一个因子,这个数的贡献就是这两个数相乘。


数字最大只有10000,用10000个vector记录每个数字的位置。

然后遍历原始序列,对于第i个数,枚举它的所有因数(这个预处理),二分分别搜索这个因子在i左边最近的和右边最近的两个位置lp,rp。然后对于所有因子产生的这些lp,rp,求一个最小的区间交,就是第i个数左右能延展的范围。


代码:

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#include <set>#include <map>#define LL long longint a[100005];map<int,int> l;map<int,int> r;vector <int> K[10005];vector <int> P[10005];const LL mod = 1e9+7;int N;int getl(int pos,int n){    int l=0,r=P[n].size()-1,res=pos;    while(l<=r){        int mid=(l+r)/2;        if(P[n][mid]<pos){            l=mid+1;            res=P[n][mid];        }        else {            r=mid-1;        }    }    if(res==pos) res=0;    else res++;    return res;}int getr(int pos,int n){    int l=0,r=P[n].size()-1,res=pos;    while(l<=r){        int mid=(l+r)/2;        if(P[n][mid]>pos){            r=mid-1;            res=P[n][mid];        }        else {            l=mid+1;        }    }    if(res==pos) res=N-1;    else res--;    return res;}int main(){    for(int i=1;i<=10000;i++){        for(int j=1;j<=sqrt(i);j++){            if(i%j==0){                K[i].push_back(j);                if(j!=i/j) K[i].push_back(i/j);            }        }    }    while(~scanf("%d",&N)){        for(int i=0;i<=10000;i++){            P[i].clear();        }        for(int i=0;i<N;i++){            scanf("%d",&a[i]);            P[a[i]].push_back(i);        }        LL res=0;        for(int i=0;i<N;i++){            LL ls=0,rs=N-1;            for(int j=0;j<K[a[i]].size();j++){                LL lp=getl(i,K[a[i]][j]);                LL rp=getr(i,K[a[i]][j]);                ls=max(ls,lp);                rs=min(rs,rp);            }            res+=((i-ls+1)*(rs-i+1));            res%=mod;        }        printf("%I64d\n",res);    }    return 0;}


0 0