BZOJ 2119: 股市的预测 SA

2119: 股市的预测

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 434  Solved: 200
[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

题意:趋势跟斜率有关,既然单位时间为1,那斜率只与Δy有关,差分之后再丢掉第一个点,变成了求 ABA 这样子串个数。|B|=m,|A|>0

想法:求ABA,先想到两个暴力:

1、枚举区间,SA+ST O(1)判断。O(n^2)

2、枚举长度以及起点,O(n^2)

然后优化第二个:不枚举起点。每隔 L 长度就设置一个哨兵。如果枚举起点的话,所有ABA中第一个A都会经过这个哨兵,并且是连续的。

于是可以这样统计多少个起点合法:L=lcs([1...x],[1...x+l+m]),R=lcp([x....n],[x+l+m...n]。防止越界没有保证|B|=m,L=min(L,l),R=min(R,l)

ans+=L+R-l。

所以一个哨兵O(1)回答,共(n/1+n/2+n/3.....1)≈O(nlogn)个哨兵。最后复杂度O(nlogn)。

记得要离散....

#include<cstdio>
#include<cstring>
#include<algorithm>

typedef long long ll;
const int len(50000);
ll a[len+10],d[len+10],ans;
int n,B,id[len+10];
struct AXLE
{
    ll a[len+10];int up;
    void deal()
    {
        for(int i=1;i<=n;i++)a[++up]=d[i];
        std::sort(a+1,a+1+up);
        int _up=1;
        for(int i=2;i<=up;i++)if(a[i]!=a[i-1])a[++_up]=a[i];
        up=_up;
    }
    int two(ll x)
    {
        int ans=up;
        for(int l=1,r=up,mid;l<=r;)
        if(a[mid=(l+r)>>1]<=x)l=mid+1,ans=mid;else r=mid-1;
        return ans;
    }
}axle;

int cnt[len+10],tmp[len+10],p[len+10],f[17][len+10],logg[len+10];
void swap(int &x,int &y){x^=y;y^=x;x^=y;}
int min(int a,int b){return a>b?b:a;}
int max(int a,int b){return a<b?b:a;}
void Fdeal()
{
    logg[0]=-1;
    for(int i=1;i<=n;i++)logg[i]=logg[i>>1]+1,f[0][i]=i+1;
    for(int j=1;j<=logg[n];j++)
    for(int i=1;i<=n;i++)f[j][i]=f[j-1][ f[j-1][i] ];
}
struct SA
{
    int str[len+10],now,limt;
    int sfa[len+10],rank[len+10],height[len+10];
    void put(int ty)
    {
        if(ty)for(int i=1;i<=n;i++)str[i]=d[i];
        else for(int i=1;i<=n;i++)str[i]=d[n-i+1];
        now=n;limt=axle.up;
//        fprintf(stderr,"now=%d\n",now);
    }
    bool cmp(int x,int y,int l){return x+l<=now&&y+l<=now&&rank[x]==rank[y]&&rank[x+l]==rank[y+l];}
    void doubling()
    {
//        fprintf(stderr,"doubling\n");
//        fprintf(stderr,"now=%d\n",now);
        for(int i=1;i<=now;i++)rank[i]=str[i],sfa[i]=i;
        for(int l=0,pos=0,sigma=limt;pos<now;sigma=pos)
        {
            pos=0;
            for(int i=now-l+1;i<=now;i++)p[++pos]=i;
            for(int i=1;i<=now;i++)if(sfa[i]>l)p[++pos]=sfa[i]-l;
            memset(cnt,0,sizeof(int)*(sigma+1)); pos=0;
            for(int i=1;i<=now;i++)cnt[rank[i]]++;
            for(int i=1;i<=sigma;i++)cnt[i]+=cnt[i-1];
            for(int i=now;i>=1;i--)sfa[cnt[rank[p[i]]]--]=p[i];
            for(int i=1;i<=now;i++)tmp[sfa[i]]=cmp(sfa[i],sfa[i-1],l)?pos:++pos;
            for(int i=1;i<=now;i++)rank[i]=tmp[i];
            l=!l?1:l<<1;
        }
        for(int i=1;i<=now;i++)rank[sfa[i]]=i;
        for(int i=1,j,k;i<=now;i++)
        {
            k=sfa[rank[i]-1]; if(!k)continue;
            j=height[rank[i-1]];    if(j)j--;
            while(str[i+j]==str[k+j])
                j++;
            height[rank[i]]=j;
        }
//        fprintf(stderr,"now=%d\n",now);
//        for(int i=1;i<=now;i++)
//        fprintf(stderr,"h=%d\n",height[i]);
    }
    int g[17][len+10];
    //lcp(x,y)=min[x,y-1]
    void Gdeal()
    {
        for(int i=1;i<=now;i++)g[0][i]=height[i+1];
        for(int j=1;j<=logg[now];j++)
        for(int i=1;i<=now;i++) g[j][i]=min(g[j-1][i],g[j-1][ f[j-1][i] ]);
    }
    int lcp(int x,int y)
    {
//        fprintf(stderr,"1:x= %d y=%d\n",x,y);
        x=rank[x];y=rank[y];
        if(x>y)swap(x,y); y--;
//        fprintf(stderr,"2:x= %d y=%d\n",x,y);
//        for(int i=x+1;i<=y;i++)
//        fprintf(stderr,"3:h=%d\n",height[i]);
        int k=logg[y-x+1];
        int w=1<<k;
        return min(g[k][x],g[k][y-w+1]);
    }
    void build(int ty)
    {
        put(ty); doubling(); Gdeal();
    }
}pre,suc;
int main()
{
    scanf("%d %d",&n,&B);
    for(int i=1;i<=n;i++)scanf("%lld",a+i),d[i-1]=a[i]-a[i-1];
    n--;axle.deal();
//    fprintf(stderr,"n=%d B=%d\n",n,B);
    for(int i=1;i<=n;i++)d[i]=axle.two(d[i]);
    Fdeal(); pre.build(0); suc.build(1);
    for(int l=1,LL,RR;l+l+B<=n;l++)
        for(int i=l,x,y;i+l+B-1<=n;i+=l)
        {
//            fprintf(stderr,"x= %d y= %d\n",i,i+l+B);
            x=i; y=i+l+B;
            LL=pre.lcp(n-y+1,n-x+1); RR=suc.lcp(x,y);
//            fprintf(stderr,"LL= %d RR= %d l=%d\n",LL,RR,l);
            ans+=max(0,min(LL,l)+min(RR,l)-l);
//            fprintf(stderr,"ans=%lld\n",ans);
        }
    printf("%lld",ans);
    return 0;
}
时间: 2024-08-08 14:25:52

BZOJ 2119: 股市的预测 SA的相关文章

BZOJ 2119: 股市的预测 [后缀数组 ST表]

2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 331  Solved: 153[Submit][Status][Discuss] Description 墨墨的妈妈热爱炒股,她要求墨墨为她编写一个软件,预测某只股票未来的走势.股票折线图是研究股票的必备工具,它通过一张时间与股票的价位的函数图像清晰地展示了股票的走势情况.经过长时间的观测,墨墨发现很多股票都有如下的规律:之前的走势很可能在短时间内重现!如图可以看到这只股票A

BZOJ 2119: 股市的预测

Description 求形如ABA形式的字符串,其中B长度固定,\(n\leqslant 10^5\) Solution 后缀数组. 我们可以枚举一个长度\(x\),然后将序列分组,每组长度为\(x\),然后从\(i\)找和\(i+x+B\)的最长公共后缀和最长公共前缀,然后得到一组合法区间,限制一下在块中防止重复即可. 复杂度就是调和级数. 复杂度\(O(nlogn)\) Code /******************************************************

●BZOJ 2119 股市的预测

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2119 题解: 这个题很好的. 首先把序列转化为差分序列,问题转化为找到合法的子序列,使得去除最中间的 M长度,剩下的头尾完全相同. 枚举重现的长度 len,然后在序列中每len个长度打一个标记,不难发现,如题所述的A部分一定只包含一个标记点.然后枚举每个被标记的点 i,得到对应的 j=i+len+M,然后求出 i和 j 向前向后可匹配的最大长度 L,R那么对答案的贡献即为 max(0,(m

BZOJ 2119 股市的预测 后缀数组

题目大意:给定一个序列,求差分后有多少个子串满足形式为ABA,其中B部分长度为m,A部分长度大于0 首先枚举A的长度j,将序列上每隔j个点插入一个关键点 对于第i个位置上的关键点,我们找到第i+j+m个位置 利用后缀数组找出两个位置向左拓展多少个位置都是相同的,以及向右拓展都少个位置都是相同的 为了保证不重复向左和向右最多拓展j-1个位置 设拓展之后长度为len,那么如果len>=j,ans+=(len-j+1) 如图,拓展出的区域长度为5,j=4,则可以找到两个子串 其中两个a虽然相同,但是由

bzoj2119 股市的预测

bzoj 题解: 后缀数组+RMQ 有一个性质是,若出现ABA形式,且|A|比较大|B|<=m,那么真正的B块端点可以来回滑动. 因此我们可以正反做两遍后缀数组,利用RMQ求出区间最小值即前缀长. 然后先枚举|A|,再枚举左边A的端点,这样ABA位置大体确定. 然后在两个左端点处分别向两端延伸,更新答案. 时间复杂度O(nlnn)? 代码: #include<cmath> #include<cstdio> #include<cstring> #include<

bzoj 3230: 相似子串【SA+st表+二分】

总是犯低级错误,st表都能写错-- 正反分别做一遍SA,预处理st表方便查询lcp,然后处理a[i]表示前i个后缀一共有多少个本质不同的子串,这里的子串是按字典序的,所以询问的时候直接在a上二分排名就能得到询问区间,然后用正反st表查lcp即可 #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=200005; int n,q,b[N],sa1[

股市潜规则 你经历过几个?(转)

1.看好不买一直涨,追涨买后变熊样! 2.气愤不过卖掉:卖后立即涨! 3.两选一必然选错,买的下跌,没买的大涨! 4.选错后改正错误,换股,又换错! 5.下决心不搞短线,长期持股,则长期不涨! 6.抛了长线,第二天涨停! 7.又去搞短线,立即被套! 8.给别人推荐的涨,自己手里的跌. 点评:股票是风险性资产,因此股票投资者在做投资决策之前,必须衡量自己承担风险的能力,以免遭受过度的损失,或破坏了这一市场的信用. 网友声音—— 溪水金桥:读完这8句话,发现都在我身上发生过.按说应该悲催了,可我边读

股市集合竞价藏了很多秘密

一.9:15----9:20这五分钟开放式集合竞价可以委托买进和卖出的单子,你看到的匹配成交量可能是虚假的,因这5分钟是可以撤单,很多主力在9:19:30左右撤单,当你买进时,你不撤单,他可撤出,然后他卖给你,因此你一定要把撤单键放在手上. 二.9:20---9:25这五分钟开放式集合竞价可以输委托买进和卖出的单子,但不能撤单,有的投资者认为他己撤单就完事了,事实上这五分钟撤单是无效的.这五分钟你看到的委托是真实的,因此要抢涨停板的,一定要看准这五分钟,但你不知道这五分钟哪些股票要涨停板,利用按

提升大数据数据分析性能的方法及技术(二)

上部分链接 致谢:因为我的文章之前是在word中写的,贴过来很多用mathtype编辑的公式无法贴过来,之前也没有经验. 参考http://www.cnblogs.com/haore147/p/3629895.html,一文完成公式的迁移. 同时,说一句,word中用mathtype写的公式用ALT+\可以转换成对应的latex语法公式. 5 数据流过滤技术 信息大爆炸时代的到来使得针对数据进行深层次的挖掘成为数据处理的核心任务[21].但是在上面已经提到了,源数据的来源和数据的组成格式都是各种