bzoj 2119: 股市的预测 (后缀数组+差分+st表)

2119: 股市的预测

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 283   Solved: 135
[ Submit][ Status][ Discuss]

Description

墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势。股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况。经过长时间的观测,墨墨发现很多股票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A部分的股价和C部分的股价的走势如出一辙。通过这个观测,墨墨认为他可能找到了一个预测股票未来走势的方法。进一步的研究可是难住了墨墨,他本想试图统计B部分的长度与发生这种情况的概率关系,不过由于数据量过于庞大,依赖人脑的力量难以完成,于是墨墨找到了善于编程的你,请你帮他找一找给定重现的间隔(B部分的长度),有多少个时间段满足首尾部分的走势完全相同呢?当然,首尾部分的长度不能为零。

Input

输入的第一行包含两个整数N、M,分别表示需要统计的总时间以及重现的间隔(B部分的长度)。接下来N行,每行一个整数,代表每一个时间点的股价。

Output

输出一个整数,表示满足条件的时间段的个数

Sample Input

12 4
1 2 3 4 8 9 1 2 3 4 8 9

Sample Output

6
【样例说明】
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

[ Submit][ Status][ Discuss]

题解: 后缀数组+差分+st表

将高度差分、离散之后,问题可以转化为:一个长度为n-1的序列,求有多少子串满足ABA的形式,并且满足|A|>0,|B|=m。 
于是我们可以枚举A的长度j,然后将整个序列分块,每块的大小为j,每块的关键点为块首的点。枚举每一个关键点i,取左端点l=i,右端点 r=i+j+m,求出后缀子串[l,n-1]和[r,n-1]的lcp最长公共前缀,以及前缀子串[1,l]和[1,r]的lcs最长公共后缀,即从lr这两个点最多能向左和向右扩展多少个点使得扩展出的两个字符串完全相同。假设对于l,向左最大能扩展到x点,向右最大能扩展到y点,左端点l在[x,y]内滑动时的长度为j的每一个子串都是满足条件的。即如果求出的lcp+lcm>=j,ans+=(lcp+lcs-j+1)。 
需要注意的是,对于每一个点i,向左和向右都最多扩展j-1个点,因为不能扩展到上一个或下一个关键点,避免重复计算。还有当lcp和lcs都不为0时,点i会被计算2次,应该在答案中减去。

时间复杂度O(N/1+N/2+N/3+N/((N-M)/2))=O(NlogN),对于静态的区间极值问题可以用st表预处理然后o(1)查询,当然不嫌麻烦可以用线段树实现。对于求解lcp和lcs可以原串和倒置串分别求后缀数组得到。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<cmath>  
#include<algorithm>  
#define N 100003  
#define LL long long 
using namespace std;  
int m,n,cnt,p,q,k,size;  
int c[N],a[N],b[N],s[N],s1[N],h[N],h1[N],v[N],sa[3][N],rank[3][N];  
int sa1[3][N],p1,q1,k1,rank1[3][N],st[20][N],st1[20][N],v1[N];  
int l[N];  
int cmp(int x,int y)  
{  
    return c[x]<c[y];  
}  
void calcsa(int sa[N],int rank[N],int SA[N],int Rank[N])  
{  
     for (int i=1;i<=cnt;i++)    
     v[rank[sa[i]]]=i;    
    for (int i=cnt;i>=1;i--)     
     if (sa[i]>k)   SA[v[rank[sa[i]-k]]--]=sa[i]-k;    
    for (int i=cnt-k+1;i<=cnt;i++)    
     SA[v[rank[i]]--]=i;    
    for (int i=1;i<=cnt;i++)    
     Rank[SA[i]]=Rank[SA[i-1]]+(rank[SA[i]]!=rank[SA[i-1]]||rank[SA[i]+k]!=rank[SA[i-1]+k]);    
}  
void calcsa1(int sa[N],int rank[N],int SA[N],int Rank[N])  
{  
    for(int i=1;i<=cnt;i++) v1[rank[sa[i]]]=i;  
    for (int i=cnt;i>=1;i--)  
     if (sa[i]>k1)  
      SA[v1[rank[sa[i]-k1]]--]=sa[i]-k1;  
    for (int i=cnt-k1+1;i<=cnt;i++)  
     SA[v1[rank[i]]--]=i;  
    for (int i=1;i<=cnt;i++)  
     Rank[SA[i]]=Rank[SA[i-1]]+(rank[SA[i]]!=rank[SA[i-1]]||rank[SA[i]+k1]!=rank[SA[i-1]+k1]);  
}  
void work()  
{  
    p=0; q=1;  
    for (int i=1;i<=cnt;i++) v[s[i]]++;  
    for (int i=1;i<=size;i++) v[i]+=v[i-1];  
    for (int i=1;i<=cnt;i++)  
      sa[p][v[s[i]]--]=i;  
    for (int i=1;i<=cnt;i++)  
     rank[p][sa[p][i]]=rank[p][sa[p][i-1]]+(s[sa[p][i]]!=s[sa[p][i-1]]);  
    k=1;  
    while(k<cnt)  
    {  
        calcsa(sa[p],rank[p],sa[q],rank[q]);  
        p^=1; q^=1; k<<=1;  
    }   
}  
void work1()  
{  
    p1=0; q1=1;  
    for (int i=1;i<=cnt;i++) v1[s1[i]]++;  
    for (int i=1;i<=size;i++) v1[i]+=v1[i-1];  
    for (int i=1;i<=cnt;i++)  
      sa1[p1][v1[s1[i]]--]=i;  
    for (int i=1;i<=cnt;i++)  
     rank1[p1][sa1[p1][i]]=rank1[p1][sa1[p1][i-1]]+(s1[sa1[p1][i]]!=s1[sa1[p1][i-1]]);  
    k1=1;  
    while(k1<cnt)  
    {  
        calcsa1(sa1[p1],rank1[p1],sa1[q1],rank1[q1]);  
        p1^=1; q1^=1; k1<<=1;  
    }   
}  
void get_height()  
{  
    k=0;  
    for (int i=1;i<=cnt;i++)  
     if (rank[p][i]==1)  h[rank[p][i]]=0;  
    else  
    {  
        int j=sa[p][rank[p][i]-1];  
        while (s[j+k]==s[i+k]&&i+k<=cnt&&j+k<=cnt)  k++;  
        h[rank[p][i]]=k;  
        if (k>0) k--;  
    }  
}  
void get_height1()  
{  
    k=0;  
    for (int i=1;i<=cnt;i++)  
     if (rank1[p1][i]==1)  h1[rank1[p1][i]]=0;  
    else  
    {  
        int j=sa1[p1][rank1[p1][i]-1];  
        while (s1[j+k]==s1[i+k])  k++;  
        h1[rank1[p1][i]]=k;  
        if (k>0) k--;  
    }  
}  
void solve()  
{  
    for (int i=1;i<=cnt;i++) st[0][i]=h[i];  
    for (int j=1;j<=20;j++)  
     for (int i=1;i<=cnt;i++)  
      if (i+(1<<j)-1<=cnt)    
       st[j][i]=min(st[j-1][i],st[j-1][i+(1<<(j-1))]);  
    for (int i=1;i<=cnt;i++) st1[0][i]=h1[i];  
    for (int j=1;j<=20;j++)  
     for (int i=1;i<=cnt;i++)  
      if (i+(1<<j)-1<=cnt)    
       st1[j][i]=min(st1[j-1][i],st1[j-1][i+(1<<(j-1))]);  
    int k=0;  
    for (int i=1;i<=cnt;i++)  
     {  
        if ((1<<(k+1))<=i)  k++;  
        l[i]=k;  
     }  
}  
int calc(int x,int y)  
{  
    if (x>y)  swap(x,y);  
    int k=l[y-x]; x++;  
    int t=min(st[k][x],st[k][y-(1<<k)+1]);  
    return t;
}  
int calc1(int x,int y)  
{  
    if (x>y)  swap(x,y);  
    int k=l[y-x]; x++;  
    return min(st1[k][x],st1[k][y-(1<<k)+1]);  
}  
int main()  
{  
    freopen("a.in","r",stdin);  
    scanf("%d%d",&n,&m);  
    for (int i=1;i<=n;i++)  
    {  
        scanf("%d",&a[i]);  
        if (i>1)  c[++cnt]=a[i]-a[i-1],b[cnt]=cnt;  
    }  
    sort(b+1,b+cnt+1,cmp);  
    c[0]=-2147483647;  
    size=0;  
    for (int i=1;i<=cnt;i++)  
     if (c[b[i]]!=c[b[i-1]])  
      ++size,s[b[i]]=size;  
     else s[b[i]]=size;  
    work();  
    for (int i=cnt;i>=1;i--)   
     s1[cnt-i+1]=s[i];  
    work1();  
    get_height(); get_height1();  
    //for (int i=1;i<=cnt;i++)
     //cout<<h1[i]<<" ";
    //cout<<endl;
    solve();  
    LL ans=0;  
    for (int len=1;len+len+m<=cnt;len++)  
    {  
        for (int i=1;i+len+m<=cnt;i+=len)  
        {  
            int j=i+len+m;   
            int t=min(len,calc(rank[p][i],rank[p][j]));  
            int t1=min(len,calc1(rank1[p1][cnt-i+1],rank1[p1][cnt-j+1]));
            if (t&&t1)  t1--;
			//cout<<i<<" "<<j<<" "<<t<<endl;
			//cout<<cnt-i+1<<" "<<cnt-j+1<<" "<<t1<<endl;   
            if (t+t1>=len)  ans+=(LL)(t+t1-len+1);  
        }  
    }  
    printf("%lld\n",ans);  
}  



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值