bzoj 2119: 股市的预测
Description
墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势。股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况。经过长时间的观测,墨墨发现很多股票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A部分的股价和C部分的股价的走势如出一辙。通过这个观测,墨墨认为他可能找到了一个预测股票未来走势的方法。进一步的研究可是难住了墨墨,他本想试图统计B部分的长度与发生这种情况的概率关系,不过由于数据量过于庞大,依赖人脑的力量难以完成,于是墨墨找到了善于编程的你,请你帮他找一找给定重现的间隔(B部分的长度),有多少个时间段满足首尾部分的走势完全相同呢?当然,首尾部分的长度不能为零。
Input
输入的第一行包含两个整数N、M,分别表示需要统计的总时间以及重现的间隔(B部分的长度)。接下来N行,每行一个整数,代表每一个时间点的股价。
Output
输出一个整数,表示满足条件的时间段的个数
Sample Input
1 2 3 4 8 9 1 2 3 4 8 9
Sample Output
【样例说明】
6个时间段分别是:3-9、2-10、2-8、1-9、3-11、4-12。
HINT
对于100%的数据,4≤N≤50000 1≤M≤10 M≤N 所有出现的整数均不超过32位含符号整数。
Source
这个题好劲啊。。。蒟蒻只会打枚举长度和起点的n^2暴力,只有40分。。。
看到了这个形式的暴力,我竟然毫无想法,这不就是和重复旋律4一样的套路吗;
枚举长度L,只考虑L的整数倍的位置,然后对于i和i+m+L,设i为l,i+m+L为r,我们需要求出l和r往左扩展和往右扩展的最大长度;
往右扩展的最大长度为[l,len]和[r,len]这两个后缀的lcp,往左扩展的最大长度为[1,l],[1,r]这两个前缀的lcs,这个可以翻转后变为lcp;z
则长度为lcp+lcs-1(lcp和lcs有一个为0的时候不要减1)的这一段都是相同的,计算一下答案(注意可能算重,所以lcp和lcs的长度要和L取min);复杂度nlogn;
还有一个小坑点,差分数组全为0的时候,那么height数组会求萎,都+N就好了。。。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define RG register #define ll long long using namespace std; const int N=1e6+10; const int Inf=19260817; struct data{ int fir,sec,id; }x[N]; int sa[N],y[N],rnk[N],rk,height[N],len,pre[N],pre2[N],ST[N][20],n,m; int b[N],a[N]; bool cmp(const data &a,const data &b){ if(a.fir==b.fir) return a.sec<b.sec; else return a.fir<b.fir; } void work2(){ rk=1;y[x[1].id]=rk; for(RG int i=2;i<=len;i++){ if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++; y[x[i].id]=rk; } } void work(){ sort(x+1,x+1+len,cmp);work2(); for(RG int i=1;i<=len;i<<=1){ for(RG int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j; for(RG int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j; sort(x+1,x+1+len,cmp);work2(); if(rk==len) break; } for(int i=1;i<=len;i++) sa[y[i]]=i; } void get_height(){ int kk=0;for(RG int i=1;i<=len;i++) rnk[sa[i]]=i; for(RG int i=1;i<=len;i++){ if(kk) kk--; int j=sa[rnk[i]-1]; while(a[i+kk]==a[j+kk]) kk++; height[rnk[i]]=kk; } } void make_ST(){ pre[0]=1;for(int i=1;i<=16;i++) pre[i]=pre[i-1]<<1; pre2[0]=-1;for(int i=1;i<=len;i++) pre2[i]=pre2[i>>1]+1; for(RG int i=2;i<=len;i++) ST[i][0]=height[i]; for(RG int j=1;j<=16;j++) for(RG int i=2;i<=len;i++){ if(i+pre[j]-1<=len){ ST[i][j]=min(ST[i][j-1],ST[i+pre[j-1]][j-1]); } } } int query(int l,int r){ if(l>r) swap(l,r); int x=pre2[r-l+1]; return min(ST[l][x],ST[r-pre[x]+1][x]); } int LCP(int l,int r){ if(l==r) return len-sa[l]; if(l>r) swap(l,r); return query(l+1,r); } int main(){ scanf("%d%d",&n,&m);n--; for(RG int i=1;i<=n+1;i++) scanf("%d",&b[i]); for(RG int i=1;i<=n;i++) a[i]=b[i+1]-b[i]+N; a[n+1]=Inf;len=n*2+1;for(int i=1;i<=n;i++) a[len-i+1]=a[i]; for(RG int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]; work();get_height();make_ST();int ans=0; for(RG int L=1;L<=(n-m)/2;L++){ for(RG int i=1;i+L+m<=n;i+=L){ int l=i,r=i+m+L; int lcp=min(L,LCP(rnk[l],rnk[r])); int lcs=min(L,LCP(rnk[len-l+1],rnk[len-r+1])); int le; if(lcp&&lcs) le=lcp+lcs-1; else le=lcp+lcs; if(le>=L) ans+=le-L+1; } } printf("%d\n",ans); }