• 【BZOJ 2119】股市的预测(SAM)


    SAM很好用的啊。。。

    传送门

    双倍经验:L-Gap Substrings

    基本做法类似,这道题的差分改掉,map 改掉就好了QWQ

    noteskey

    反正就是先差分一下,然后把首项丢掉(没有比较的对象自然就不算趋势了)

    然后就是建 SAM ,做法如下(抄了自己的题解 QWQ)

    转化:给出一个序列,求多少个相距为 m 的子串是相同的

    因为 v 的长度是已知的 m ,我们甚至都不用在乎 v 是什么,只需要找到有多少个相距为 m 的相同字符串就好了

    枚举长度

    于是我们考虑和优秀的拆分(就是可以暴力 hash 水的那道)一样的做法,枚举两个相同字符串的长度 j

    然后我们枚举到一个位置 l 之后, r 其实就确定了, (r=l+m+j) ,然后我们求出以 l、r 结尾的 lcs ,以及以 l、r 开头的 lcp 的长度,分别记为 lcs、lcp (就这么标注吧...)

    这样的话我们发现只要 lcs+lcp-1>=j ,(j 也就是我们枚举的长度),那么当前的 l、r 就是一个可行解

    这里为什么要 -1 ?因为我们计算 lcp 和 lcs 的时候 l、r 位置的字母算了两次啊...

    至于 lcp 等于 0 的话,lcs 必然也等于 0 ,这时候必然不满足条件,得到长度为 -1 也没关系...

    一点优化

    但是我们发现这样的复杂度有点锅...(这不是已经 n^2 了么...) 那么我们发现求出来的 lcp 和 lcs 没有充分利用,仅仅是拿来判断当前 l、r 是否可行了

    其实我们可以让 lcp 和 lcs 分别与 j 取 min ,然后判断某一块区域解的合法性,因为 l、r 一起左右移动的话,中间的距离是不变的啊...

    这样的话我们算出当前长度为 2j 的区域的 u 长度为 j 的解,然后跳到下一个区域就好了

    所以说 (lcp+lcs-1-j+1=lcp+lcs-j) 就是当前可以计入答案的解

    至于为什么要和 j 取 min ? 因为我们下一次要让 l 加上 j 跳到下一个区域,和 j 取 min 可以保证答案不算重

    这样做的复杂度 是 (O({nover 1}+{nover 2}+...+{nover n})=O(n({1}+{1over 2}+...+{1over n}))≈O(n~log~n)) ,只能说是这个级别的复杂度,证明需要用调和级数吧(我不会)

    求 lcp 和 lcs

    那么接下来的任务也就是求 lcp 和 lcs 的长度了

    我们都知道一个字符串内 两个子串的 lcp 就等于他们 (endpos) 所在节点的 lca ,然后 lcp 长度就是 (len[lca]) 了,那么我们把 parent 树建出来,就可以开森的在上面树剖找 lca 啦~

    这样的复杂度是... (O(n~log^2~n))

    好卡啊...那么我们用 st 表优化一下求 lca 可能就是一个 (O(n~log~n)) 的算法了吧...

    watch out

    数组别忘了清零...这个我倒是没犯

    但是我 TM 调了一个下午的原因就是 (insert) 的时候 (s[i]) 没有减去 $'a' $ ,结果数组越界出现了无限可能啊!mmp,千万不要学我这个代码都打不来的大菜鸡...

    卡常的话这道题根本不需要(那些技巧留着做 Ynoi 吧~)

    code

    可读性极差,不建议 copy

    //by Judge
    #include<map>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define Rg register
    #define fp(i,a,b) for(Rg int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(Rg int i=(a),I=(b)-1;i>I;--i)
    #define go(G,u) for(Rg int i=G.head[u],v=G.e[i].to;i;v=G.e[i=G.e[i].nxt].to)
    #define ll long long
    using namespace std;
    const int M=2e5+3;
    typedef int arr[M];
    #ifndef Judge
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    #endif
    char buf[1<<21],*p1=buf,*p2=buf;
    inline int read(){ int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    } inline void reads(string& s){ char c=getchar();
    	for(;!isalpha(c);c=getchar()); s=" ";
    	for(;isalpha(c);c=getchar()) s+=c;
    } int n,m,a[M]; ll ans;
    struct SAM{ int cnt,las,tim; SAM(){las=cnt=1;}
    	int st[M][19]; arr fa,len,lg,pos,id; map<int,int> to[M];
    	inline void insert(int c,int now){
    		int p=las,np=las=++cnt; len[np]=len[p]+1;
    		for(;p&&!to[p][c];p=fa[p]) to[p][c]=np; pos[now]=np;
    		if(!p) return fa[np]=1,void(); int q=to[p][c];
    		if(len[q]==len[p]+1) return fa[np]=q,void();
    		int nq=++cnt; len[nq]=len[p]+1,fa[nq]=fa[q];
    		fa[q]=fa[np]=nq,to[nq]=to[q];
    		for(;p&&to[p][c]==q;p=fa[p]) to[p][c]=nq;
    	}
    	struct Gr{ int pat,head[M]; struct Edge{ int to,nxt; }e[M];
    		inline void add(int u,int v){e[++pat]={v,head[u]},head[u]=pat;}
    	}G;
    	inline int Min(int x,int y){return len[x]<len[y]?x:y;}
    	void dfs(int u){ st[++tim][0]=u,id[u]=tim; go(G,u) dfs(v),st[++tim][0]=u; }
    	inline void calc(){ fp(i,1,cnt) G.add(fa[i],i); dfs(1); fp(i,2,tim) lg[i]=lg[i>>1]+1;
    		fp(j,1,lg[tim]) fp(i,1,tim-(1<<j)+1) st[i][j]=Min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    	}
    	inline int get(int x,int y,int k=0){
    		x=id[pos[x]],y=id[pos[y]]; if(x>y) swap(x,y); k=lg[y-x+1];
    		return len[Min(st[x][k],st[y-(1<<k)+1][k])];
    	}
    }p,q;
    inline int Min(int x,int y){return x<y?x:y;}
    int main(){ n=read()-1,m=read();
    	fp(i,0,n) a[i]=read();
    	fd(i,n,1) a[i]-=a[i-1];
    	fp(i,1,n) p.insert(a[i],i);
    	fd(i,n,1) q.insert(a[i],i);
    	p.calc(),q.calc();
    	fp(j,1,(n-m)>>1)
    		for(Rg int i=1;i+j+m<=n;i+=j){
    			int l=i,r=i+j+m;
    			int lcp=Min(q.get(l,r),j);
    			int lcs=Min(p.get(l,r),j);
    			int len=lcs+lcp-1;
    			if(len>=j) ans+=len-j+1;
    		} return !printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    [Coding Made Simple] Coin Changes Minimum Number of Coins
    [Coding Made Simple] Egg Dropping
    015_配置免密登录
    014_编写批量修改扩展名脚本,如批量将 txt 文件修改为 doc 文件
    013_使用 user.txt 文件中的人员名单,在计算机中自动创建对应的账户并配置初始密码
    012_使用死循环实时显示 eth0 网卡发送的数据包流量
    011_9*9 乘法表(编写 shell 脚本,打印 9*9 乘法表)
    010_编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机状态
    09_编写脚本,实现人机<石头,剪刀,布>游戏
    08_依次提示用户输入 3 个整数,脚本根据数字大小依次排序输出 3 个数字
  • 原文地址:https://www.cnblogs.com/Judge/p/10666952.html
Copyright © 2020-2023  润新知