【AT4163】[ARC099D] Eating Symbols Hard(哈希)
- 有一个初始全为\(0\)的数组一个初始在\(0\)的指针。
- 给定一个长度为\(n\)的操作串,有四种操作符:"<"表示将指针左移一位,">"表示将指针右移一位,"+"表示将指针对应位置加\(1\),"-"表示将指针对应位置减\(1\)。
- 求有多少子串,使得执行其中操作得到的数组和整串相同。
- \(n\le2.5\times10^5\)
哈希
我们设\(f_i(x)\)表示执行了前\(i\)位得到数组的生成函数\(\sum_{k=-\infty}^{+\infty}a_kx^k\),\(g_i(x)\)表示执行了前\(i\)位指针指向位置\(p_i\)对应的\(x^{p_i}\)。
考虑一个操作符的转移。
如果是"<"或">",则\(f_i(x)=f_{i-1}(x)\),\(g_i(x)=g_i(x)\times x^{\mp1}\)。
如果是"+"或"-",则\(f_i(x)=f_{i-1}(x)\pm g_i(x)\),\(g_i(x)=g_{i-1}(x)\)。
然后我们只要任选两个\(x\)代入,就成为双哈希了。
答案的计算
考虑一段\([L,R]\)操作得到的生成函数应该是:
\[\frac{f_R(x)-f_{L-1}(x)}{g_{l-1}(x)}
\]
现在它需要等于\(f_n(x)\),也就是说:
\[f_n(x)=\frac{f_R(x)-f_{L-1}(x)}{g_{l-1}(x)}\Leftrightarrow f_n(x)\times g_{l-1}(x)+f_{L-1}(x)=f_R(x)
\]
因此我们从后往前枚举\(L\),\(map\)中维护好所有的\(f_R(x)\),然后询问有多少个$ f_n(x)\times g_{l-1}(x)+f_{L-1}(x)$计入答案即可。
代码:\(O(nlogn)\)
#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 250000
#define S1 324682339
#define S2 456789001
#define X 998244353
using namespace std;
int n;char s[N+5];I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
struct Hash
{
int x,y;I Hash() {x=y=0;}I Hash(CI a):x(a),y(a){}I Hash(CI a,CI b):x(a),y(b){}
I Hash operator + (Con Hash& o) Con {return Hash((x+o.x)%X,(y+o.y)%X);}
I Hash operator - (Con Hash& o) Con {return Hash((x-o.x+X)%X,(y-o.y+X)%X);}
I Hash operator * (Con Hash& o) Con {return Hash(1LL*x*o.x%X,1LL*y*o.y%X);}
I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;}
}seed,f[N+5],g[N+5],sd(S1,S2),isd(QP(S1,X-2),QP(S2,X-2));map<Hash,int> p;
int main()
{
RI i;for(scanf("%d%s",&n,s+1),g[0]=i=1;i<=n;++i) switch(s[i])//按运算符分类讨论
{
case '+':f[i]=f[i-1]+(g[i]=g[i-1]);break;case '-':f[i]=f[i-1]-(g[i]=g[i-1]);break;
case '<':f[i]=f[i-1],g[i]=g[i-1]*isd;break;case '>':f[i]=f[i-1],g[i]=g[i-1]*sd;break;
}
long long t=0;for(i=n;i;--i) ++p[f[i]],t+=p[g[i-1]*f[n]+f[i-1]];return printf("%lld\n",t),0;//从后往前枚举L统计答案
}
待到再迷茫时回头望,所有脚印会发出光芒