把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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统计答案
}
posted @ 2021-04-06 20:21  TheLostWeak  阅读(50)  评论(0编辑  收藏  举报