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

2119: 股市的预测

Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 331  Solved: 153
[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位含符号整数。



走势相同,再次用到差分

还要离散化

然后就是求有多少子串样子是ABA

然后,和上题相似的思路,枚举走势相同的长度L,每L设一个关键点

发现对于一个ABA,第一个A一定覆盖且只覆盖一个关键点

枚举关键点i,i和i+B+L往左往右匹配长度l和r(同样,我的r包括关键点),如果l+r>=L那么这个地方就有l+r-L+1个满足的子串

(i)--L--B--(i+B+L)--L--

为了做到不重复,左右延伸最大长度为L-1

为什么这样就不重复了?我这个煞笔竟然想了好长时间,因为上面的蓝色字体,每个关键点延伸出来的,第一个L一定覆盖了这个关键点,从其他关键点开始的都不覆盖,所以不重复不遗漏

注意:

1.计数排序最后是倒序

2.因为我的r包括关键点,所以最大是L不是L-1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=5e4+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
int B,s[N],mp[N],tot;
int n,m,c[N],t1[N],t2[N];
int Bin(int v){
    int l=1,r=tot;
    while(l<=r){
        int mid=(l+r)>>1;
        if(mp[mid]==v) return mid;
        else if(v<mp[mid]) r=mid-1;
        else l=mid+1;
    }
    return 0;
}
inline bool cmp(int *r,int a,int b,int j){
    return a+j<=n&&b+j<=n&&r[a]==r[b]&&r[a+j]==r[b+j];
}
int Log[N],Pow[20],mn[N][17];

void iniST(){
    Pow[0]=1;for(int i=1;i<20;i++)Pow[i]=Pow[i-1]<<1;
    Log[0]=-1;for(int i=1;i<=n;i++)Log[i]=Log[i/2]+1;
}
void getST(int mn[N][17],int a[]){
    for(int i=1;i<=n;i++) mn[i][0]=a[i];
    for(int j=1;j<=Log[n];j++)
        for(int i=1;i+Pow[j]-1<=n;i++)
            mn[i][j]=min(mn[i][j-1],mn[i+Pow[j-1]][j-1]);
}

struct SA{
    int sa[N],rnk[N],height[N],mn[N][17];

    void getHeight(){
        int k=0;
        for(int i=1;i<=n;i++) rnk[sa[i]]=i;
        for(int i=1;i<=n;i++){
            if(k) k--;
            if(rnk[i]==1) continue;
            int j=sa[rnk[i]-1];
            while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
            height[rnk[i]]=k;
        }
    }
    void getSA(){
        int *r=t1,*k=t2;
        for(int i=0;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[r[i]=s[i]]++;
        for(int i=1;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--) sa[c[r[i]]--]=i;

        for(int j=1;j<=n;j<<=1){
            int p=0;
            for(int i=n-j+1;i<=n;i++) k[++p]=i;
            for(int i=1;i<=n;i++) if(sa[i]>j) k[++p]=sa[i]-j;

            for(int i=0;i<=m;i++) c[i]=0;
            for(int i=1;i<=n;i++) c[r[k[i]]]++;
            for(int i=1;i<=m;i++) c[i]+=c[i-1];
            for(int i=n;i>=1;i--) sa[c[r[k[i]]]--]=k[i];

            swap(r,k);p=0;r[sa[1]]=++p;
            for(int i=2;i<=n;i++) r[sa[i]]=cmp(k,sa[i],sa[i-1],j)?p:++p;
            if(p>=n) break;m=p;
        }
    }

    int lcp(int x,int y){//printf("lcp %d %d\n",x,y);
        x=rnk[x];y=rnk[y];
        if(x>y) swap(x,y);x++;//printf("rnk %d %d\n",x,y);
        int t=Log[y-x+1];//printf("t %d %d %d\n",t,mn[x][t],mn[y-Pow[t]+1][t]);
        return min(mn[x][t],mn[y-Pow[t]+1][t]);
    }

    void ini(){
        getSA();getHeight();getST(mn,height);
    }
    void test(){
        puts("test");
        for(int i=1;i<=n;i++) printf("%d ",s[i]);puts("");
        for(int i=1;i<=n;i++) printf("%d ",rnk[i]);puts("");
        for(int i=1;i<=n;i++) printf("%d ",sa[i]);puts("");
        for(int i=1;i<=n;i++) printf("%d ",height[i]);puts("");
        puts("");
    }
}a,b;

int ans;
void solve(int L){//printf("sol %d\n",L);
    for(int i=1;i+B+L<=n;i+=L)
        if(s[i]==s[i+B+L]){//puts("begin");
            int r=a.lcp(i,i+B+L),l=b.lcp(n-i+2,n-i-B-L+2);
            l=min(l,L-1);r=min(r,L);

            //printf("ss %d %d %d %d\n",i,i+B+L,l,r);
            if(l+r>=L) ans+=l+r+1-L;//,printf("hi %d %d\n",i,i+B+L);
            //puts("end");
        }
}

int main(){
    //freopen("in.txt","r",stdin);
    n=read();B=read();
    for(int i=1;i<=n;i++){
        s[i]=read();
        if(i!=1) mp[++tot]=s[i-1]=s[i]-s[i-1];
    }
    n--;
    sort(mp+1,mp+1+n);
    tot=0;mp[++tot]=mp[1];
    for(int i=2;i<=n;i++) if(mp[i]!=mp[i-1]) mp[++tot]=mp[i];
    for(int i=1;i<=n;i++) s[i]=Bin(s[i]);
    m=n;

    iniST();
    a.ini();//a.test();
    reverse(s+1,s+1+n);
    b.ini();//b.test();
    reverse(s+1,s+1+n);
    for(int L=1;L+L+B<=n;L++) solve(L);
    printf("%d",ans);
}
时间: 2024-10-24 18:39:45

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

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虽然相同,但是由

POJ 3693 Maximum repetition substring(后缀数组+ST表)

[题目链接] poj.org/problem?id=3693 [题目大意] 求一个串重复次数最多的连续重复子串并输出,要求字典序最小. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+l]前缀匹配得到的最长长度为r, 枚举所有的l和i,得到r,那么答案就是r/l+1的最大值. 计算任意后缀的最长公共前缀可以利用后缀数组+ST表来解决, 两个后缀的最长公共前缀就是他们名次之间的h数组的最小值. 显然,枚举i和l的复杂度达到了O(n2),是没有办法完成统计的, 我们发现每个区段只会存

SPOJ 687 Repeats(后缀数组+ST表)

[题目链接] http://www.spoj.com/problems/REPEATS/en/ [题目大意] 求重复次数最多的连续重复子串的长度. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+l]前缀匹配得到的最长长度为r,枚举所有的l和i,得到r,那么答案就是r/l+1的最大值.计算任意后缀的最长公共前缀可以利用后缀数组+ST表来解决,两个后缀的最长公共前缀就是他们名次之间的h数组的最小值. 显然,枚举i和l的复杂度达到了O(n2),是没有办法完成统计的,我们发现每个区段只

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

[LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树) 题面 给出一个长度为\(n\)的字符串\(s\),以及\(m\)组询问.每个询问是一个四元组\((a,b,c,d)\),问\(s[a,b]\)的所有子串和字符串\(s[c,d]\)的最长公共前缀长度的最大值. \(n,m \leq 10^5\) 分析 显然答案有单调性.首先我们二分答案\(mid\),考虑如何判定. 如果mid这个答案可行,那么一定存在一个后缀x,它的开头在\([a,

BZOJ 2119: 股市的预测 SA

2119: 股市的预测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 434  Solved: 200[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

UVA 11475 Extend to Palindrome(后缀数组+ST表)

[题目链接] http://acm.hust.edu.cn/vjudge/problem/27647 [题目大意] 给出一个字符串,要求在其后面添加最少的字符数,使得其成为一个回文串.并输出这个回文串. [题解] 用拼接符将原字符串倒序相接,做一遍后缀数组,查询两串相应位置的LCP就是以该点为中心的回文串长度的一半分,奇偶求出所有后缀回文串,保留最长的,则补充部分为剩下的前缀的倒置.至于查询两串的LCP我们可以在height数组建立ST表查询. [代码] #include <cstdio> #

UVA 12338:Anti-Rhyme Pairs(后缀数组+ST表)

[题目链接] click [题目大意] 给出一些字符串,询问查询任意两个字符串的最长公共前缀 [题解] 将字符串拼接,对拼接的字符串做后缀数组,对于查询的两个字符串, 只要在height数组上查询区间最小值即可. 特别注意多组数据时候对字符串结尾的处理,很久没写容易忽视导致wa. [代码] #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=