HDU 5288 OO’s Sequence (from: 2015 Multi-University Training Contest 1)

来源:互联网 发布:淘宝如何赚集分宝 编辑:程序博客网 时间:2024/06/06 05:43
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5288

是的,题解说这是一道水题,恩恩,好难过……

题意:给一个数组,以及一个函数f(l,r),这个函数的作用是求出a数组里有多少个在[l,r]范围内的a[i],在数组a的[l,r]内没有其因子。然后叫我们求这个:


思路:暴力肯定要出事情的是不是?遇到这种求一个范围内,有多少数的题目,不妨考虑每个数对答案的贡献。

如果在一个范围内,没有ai的因子,那么a[i]在这个范围内,以及这个范围的子范围内,都是可以对答案有贡献的,设立两个数组,l[],r[],作为最接近数a[i]的左右两个因子的位置,那么ai对答案的贡献就是(r[i]-i)*(i-l[i]),所以很简单嘛!每个数找到这样两个数,记录下,答案不就出来了!
…………然后我就TLE了,难过死了= =

如果直接对每个数找左右因子的话,题目的时间复杂度是O(n^2)还是需要优化的,优化的方法?
不妨反过来思考,让每个左右因子去找离它最接近的数如何?因为一个数展开搜索要O(n)的时间且一个数只能找到对应的两个左右因子,但是,一个因子可以对应多个数,且一个因子找其对应的数只要O(√a)的时间(a为数组a中最大的数),更何况题目里明确说了a[i]<=10000,这样的方案明显优于前面的数找因子方法。
用pre[i]代表搜索过程中等于i值的a[]中的数的下标,举例说pre[i]=j的意思是a[j]=i。
先从左往右扫一遍数组,看pre[]这个数组中是否有是当前数a[i]倍数的数,有则a[i]是这个数的右因子,标记一下,题目要求的是一个数离它最近的因子,因此只要标记第一次就好了,以后就算有因子,也不进行标记。
左边的因子同理,有一个注意点是我们还要设a[0]和a[n+1]为1,这样每个数都能找到自己对应的左右因子
也许自己说的不是很好,见代码
#include <cstdio>#include <cstring>#include<iostream>using namespace std;#define MS(x,y) memset(x,y,sizeof(x))typedef long long LL;const int MAXN=1e5+5;const int MOD=1e9+7;int a[MAXN],l[MAXN],r[MAXN],pre[MAXN],last[MAXN];int main(){int n;while(~scanf("%d",&n)){for(int i=1;i<=n;++i){l[i]=-1;r[i]=-1;scanf("%d",&a[i]);}MS(pre,-1);MS(last,-1);a[0]=a[n+1]=1;for(int i=0;i<=n+1;++i){for(int j=a[i];j<MAXN;j+=a[i])if(~pre[j]&&r[pre[j]]==-1) r[pre[j]]=i;pre[a[i]]=i;}for(int i=n+1;i>=0;--i){for(int j=a[i];j<MAXN;j+=a[i])if(~last[j]&&l[last[j]]==-1) l[last[j]]=i;last[a[i]]=i;}LL ans=0;//for(int i=1;i<=n;++i) cout<<i<<": "<<l[i]<<" "<<r[i]<<endl;for(int i=1;i<=n;++i)ans=(ans+((LL)r[i]-i)*(i-l[i])%MOD)%MOD;printf("%I64d\n",ans);}}


0 0
原创粉丝点击