给定一个长 的字符串 和两个权值数组 。
定义 。
定义 ,你需要求出 。
强制在线,。
,。
讲一下等差数列做法。
只需要每次算出 的所有 border 的左端点的 的和即可。
找等差数列是简单的,对于一个长度为 的串 ,假设 也是其 border。
设 为 的 border,,则 都是 的 border,其中 。这样就能找到 所在的等差数列,递归寻找 所在的等差数列即可,这样每次长度至少减半。
赛时太困了,并没有想到怎么维护等差数列的和。实际上,对于 的情况,有 ,即 border 可以同时往里缩一个周期,那么不妨开一个哈希表 维护 ,公差为 的在 中的等差数列的和,即 。这样每次找到一个等差数列的时候前面 项一定都算好了,只需要另外开一个长度哈希表判断第 项有没有算即可。
时间复杂度 ,哈希表用 map 会变成 。
代码如下:
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
template<typename T>
inline void read(T &x)
{
x=0;
T w=1;
char c=getchar();
while(c<'0'||c>'9') w=(c=='-'?-w:w),c=getchar();
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
x*=w;
}
typedef long long ll;
const int S=1000005;
int n,t,a[S],b[S],s[S];
int f[S];
map<int,int> sm[S],le[S];
int main()
{
freopen("str.in","r",stdin);
freopen("str.out","w",stdout);
read(n),read(t);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;i++) read(s[i]);
f[1]=0;
ll ans=b[1]*a[1];
printf("%lld\n",ans);
for(int i=2,j=0;i<=n;i++)
{
s[i]=(s[i]+t*ans)&31;
while(j!=0&&s[j+1]!=s[i]) j=f[j];
if(s[j+1]==s[i]) j++;
f[i]=j;
int len=i,sma=0;
while(len>0)
{
int k=f[len];
int d=len-k,l=len/d-1;
int p=i-len+1; // p,p+d,p+2d,...,p+ld
// printf(">> %d %d: %d %d %d\n",i,len,p,d,l);
if(le[p].count(d)==0) sm[p][d]=a[p];
if(le[p][d]<l)
{
sm[p][d]+=a[p+l*d];
le[p][d]=l;
}
sma+=sm[p][d];
len=f[len-l*d];
}
ans+=1ll*sma*b[i];
printf("%lld\n",ans);
}
return 0;
}