zoukankan      html  css  js  c++  java
  • 【BZOJ2119】股市的预测(后缀数组)

    点此看题面

    大致题意: 给你一个数列,问你有多少个区间,满足中间有一段长为(m)的间隔,且首尾两端走势相同。

    前言

    被题意杀了。。。研究样例研究了半天。。。

    题意转化

    考虑走势相同,就是相邻元素两两差值相同。

    因此,如果我们存下相邻元素的差值,就变成询问有多少个区间形如(ABA)(B)的长度为(m)

    神奇做法

    对于这个,有一个非常神奇的做法。

    我们枚举(A)的长度(x),然后选取(1,x+1,2x+1,lfloorfrac nx floor imes x+1)这些点作为关键点(根据调和级数,关键点的总数是(nlogn)级别的)。

    考虑枚举每一个关键点(i)作为(ABA)中左边的(A)中的一点,那么右边的(A)的位置就应该在(j=i+m+x)

    然后我们求出(l=LCS(i,j),r=LCP(i,j))。其中(LCP)是最长公共前缀,相信大家都很熟悉,可以用后缀数组求出;而(LCS)就是最长公共后缀,其实就相当于反串的(LCP)

    也就是说,以(i)为中心,向左(l)个位置(包括(i)自身),向右(r)个位置(同样包括(i)自身),这一段区间是可以与(j)的对应区间相匹配的。

    但需要考虑我们规定了枚举的(i)是左边(A)中的一点,若(l>x)(r>x),那么就会选到其他的关键点。而两个关键点相距(x),显然不可能同时存在于一个长为(x)的串中,此时就会出现计算重复的情况。

    因此,我们需要将(l,r)(x)(min)

    (i)的贡献就是((l+r-1)-(x-1)=l+r-x),即在共计(l+r-1)个字符中选出(x)个连续字符的方案数。

    然后这道题就做完了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define LN 20 
    using namespace std;
    int n,m,a[N+5],b[N+5],dc,dv[N+5];long long ans;
    class SuffixArray//后缀数组
    {
    	private:
    		int SA[N+5],rk[N+5],p[N+5],t[N+5],H[N+5],Lg[N+5],Mn[N+5][LN+5];
    		I void Sort(CI S)
    		{
    			RI i;for(i=0;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]];
    			for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i];
    		}
    		I void GetSA(int *s)
    		{
    			RI i;for(i=1;i<=n;++i) rk[p[i]=i]=s[i];
    			RI k,t=0,S=dc;for(Sort(S),k=1;t^n;S=t,k<<=1)
    			{
    				for(t=0,i=1;i<=k;++i) p[++t]=n-k+i;
    				for(i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);
    				for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];
    				for(rk[SA[1]]=t=1,i=2;i<=n;++i)
    					rk[SA[i]]=(p[SA[i-1]]^p[SA[i]]||p[SA[i-1]+k]^p[SA[i]+k])?++t:t;
    			}
    		}
    		I void GetH(int *s)
    		{
    			RI i,j,k=0;for(i=1;i<=n;++i) p[SA[i]]=i;
    			for(i=1;i<=n;++i)
    			{
    				if(k&&--k,p[i]==1) continue;j=SA[p[i]-1];
    				W(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;H[p[i]]=k;
    			}
    			for(Lg[0]=-1,i=1;i<=n;++i) Mn[i][0]=H[i],Lg[i]=Lg[i>>1]+1;
    			for(j=1;j<=LN;++j) for(i=1;i+(1<<j)-1<=n;++i)
    				Mn[i][j]=min(Mn[i][j-1],Mn[i+(1<<j-1)][j-1]);
    		}
    	public:
    		I void Init(int *s) {GetSA(s),GetH(s);}
    		I int LCP(CI a,CI b)//RMQ求LCP
    		{
    			RI x=min(p[a],p[b])+1,y=max(p[a],p[b]),k=Lg[y-x+1];//根据Height的定义,转化为SA
    			return min(Mn[x][k],Mn[y-(1<<k)+1][k]);
    		}
    }S1,S2;
    I void GetAns(CI x)
    {
    	RI i,j,l,r;for(i=1;(j=i+m+x)<=n;i+=x)
    		l=S2.LCP(n-i+1,n-j+1),l>x&&(l=x),r=S1.LCP(i,j),r>x&&(r=x),//求出l,r,注意向x取min
    		l+r-1>=x&&(ans+=l+r-x);//计算贡献
    }
    int main()
    {
    	RI i;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",a+i);
    	for(--n,i=1;i<=n;++i) dv[i]=(a[i]-=a[i+1]);sort(dv+1,dv+n+1);
    	for(dc=unique(dv+1,dv+n+1)-dv-1,i=1;i<=n;++i)//离散化
    		a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv,b[n-i+1]=a[i];//b记录反串,用于求LCS
    	for(S1.Init(a),S2.Init(b),i=1;m+2*i<=n;++i) GetAns(i);//枚举A串长度
    	return printf("%lld
    ",ans),0;
    }
    
  • 相关阅读:
    【计算机视觉】深度相机(三)--三种方案对比
    【计算机视觉】深度相机(三)--三种方案对比
    【计算机视觉】深度相机(二)--结构光深度测距
    【计算机视觉】深度相机(二)--结构光深度测距
    【计算机视觉】深度相机(一)--TOF总结
    【计算机视觉】深度相机(一)--TOF总结
    【计算机视觉】人脸表情识别技术
    Http报头Accept与Content-Type的区别
    ajax上传文件,并检查文件类型、检查文件大小
    oracle 生成随机数【待整理】
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2119.html
Copyright © 2011-2022 走看看