【JZOJ7839】神秘代码

凯尔希我谢谢你
lcp的题所以考虑使用 $ S A $ 或者 $ SAM $
此处使用大佬提供的 $ SA $ 思路
Part I
首先我们考虑不反转怎么做
这其实是一道SA板子题
我们将所有的字符串全部用特殊符号隔开变成一个大字符串
然后把每个点的 $ height $ 数组跑出来
对于每一个点的 $ height $ 值我们独立算贡献
怎么算?
前置芝士1 : 将 $ height $ 按照 $ SA $ 跑出来的排名排序,对于任意两个后缀 $ i $ 和 $ j $,它们的 $ LCP $ 即为
所以我们就可以对于每一个 $ hi_i $ ,运用二分加 $ ST $ 表的思想,找出最左&最右边的 $ hi_j \ge $ 自己的位置
tips1:为了防止重复计算,可以左边取$ \ge $,右边取 $ > $
统计数量再乘上 $ hi_i $即可~
当然因为题目中 $ (i,j) $ 无序,$ ans $ 需要乘 $ 2 $
Part II
接下来我们考虑加上反转这个限制
对于大字符串中的每两个字符 $ c_i , c_j $ 考虑记录它们原字符串的出现概率 $ p_i $
比如一个正常串 $ abc $ 反转概率为 $ p $
变成大字符串如下
| 字符数组 | a | b | c | @ | c | b | a |
|---|---|---|---|---|---|---|---|
| 概率数组 | 1-p | 1-p | 1-p | 0 | p | p | p |
跟前面的Part结合一下
就是将概率数组前缀和一下,然后将区间的概率和乘上 $ hi_i $
思路很完美
Part III
将如上部分打出来之后会发现答案会多
想想为什么
$ Q1 : $ 正串和反串不可以同时出现!
$ Q2 : $ 算正串或者反串自己内部的贡献的时候概率多乘了!!!
考虑对 $ Q1 $ 容斥
对于每个串,在刚才答案的基础上,减去 $ ans(正串拼反串) $ 加上 $ ans(正串) + ans(反串)$
此时就可以容斥掉正反串互相勾结的情况
对 $ Q2 $ 特判,如果单正串或者单反串的话,对于当前 $ ans $ 乘回一个 $ p_i $ 的逆元
tips2:考虑加上掉选到同一位的情况,对于每一个串 $ i $ ,$ ans+= $ $ {len_i*(len_i+1) \over 2} $
就搞定啦!
故事:\(Q2\) 搞了我一个上午 & $ SA $ 再用 $ swap $ 我是狗 😒
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e6+10;
int sa[N],ton[N],rk[N],y[N],hi[N],a[N],aa[N],rmq[23][N],lg[N],p[N];
ll b[N],bb[N],ans,s[N],tot;
const int mod=998244353;
char c[N];
void Sot(int n,int m){
for(int i=0;i<=m;i++) ton[i]=0;
for(int i=1;i<=n;i++) ton[rk[y[i]]]++;
for(int i=1;i<=m;i++) ton[i]+=ton[i-1];
for(int i=n;i>=1;i--) sa[ton[rk[y[i]]]--]=y[i];
}
bool cmp(int u,int v,int w){return y[u]==y[v]&&y[u+w]==y[v+w];}
ll fp(ll x,ll y){
ll an=1;
while(y){
if(y&1) an=an*x%mod;
x=x*x%mod,y>>=1;
}
return an;
}
void SA(int n,int m){
int p=0;
for(int i=1;i<=n;i++) y[i]=i,rk[i]=a[i],sa[i]=0;
Sot(n,m);
for(int w=1;p<n;w<<=1,m=p){
p=0;
for(int i=n-w+1;i<=n;i++) y[++p]=i;
for(int i=1;i<=n;i++) if(sa[i]>w) y[++p]=sa[i]-w;
Sot(n,m);
for(int i=1;i<=n;i++) rk[i]^=y[i]^=rk[i]^=y[i];
rk[sa[1]]=p=1;
for(int i=2;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
if(p==n) break;
}
}
int ck(int l,int r){
int kk=lg[r-l+1];
return min(rmq[kk][l],rmq[kk][r-(1<<kk)+1]);
}
void h(int n){
for(int i=1,j,k=0;i<=n;i++){
for(k?--k:0,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
hi[rk[i]]=k,s[rk[i]]=b[i],rmq[0][rk[i]]=hi[rk[i]];
}
for(int i=1;i<=n;i++) s[i]=(s[i-1]+s[i])%mod;
for(int i=1;i<=22;i++) for(int j=1;j+(1<<i)-1<=n;j++) rmq[i][j]=min(rmq[i-1][j],rmq[i-1][j+(1<<i-1)]);
for(int i=2;i<=n;i++){
int l=2,r=i-1,a1=i,a2=i,mid;
while(l<=r){
mid=l+r>>1;
if(ck(mid,i-1)>hi[i]) a1=mid,r=mid-1;
else l=mid+1;
}
l=i+1,r=n;
while(l<=r){
mid=l+r>>1;
if(ck(i+1,mid)>=hi[i]) a2=mid,l=mid+1;
else r=mid-1;
}
ans=(ans+1ll*hi[i]*(s[i-1]-s[a1-2]+mod)%mod*(s[a2]-s[i-1]+mod)%mod%mod)%mod;
}
ans=ans*2ll%mod;
}
char ch;
int aw;
int rd(){
aw=ch=0;
while(ch<48||ch>57) ch=getchar();
while(ch>47&&ch<58) aw=(aw<<3)+(aw<<1)+(ch^48),ch=getchar();
return aw;
}
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
int n,m=0,o=30,k,ct,pq;
n=rd();
for(int i=2;i<N;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) p[i]=rd();
for(int i=1;i<=n;i++){
scanf("%s",c+1);
k=strlen(c+1),ct=0;
pq=(1-p[i]+mod)%mod;
for(int j=1;j<=k;j++) aa[++m]=a[++ct]=c[j]-96,bb[m]=b[ct]=pq;
aa[++m]=a[++ct]=++o,bb[m]=b[ct]=0;
SA(ct,o),h(ct);
tot=(tot+ans*fp(pq,mod-2)%mod+k*(k+1)/2)%mod,ans=0;
for(int j=k;j>=1;j--) aa[++m]=a[++ct]=c[j]-96,bb[m]=b[ct]=p[i];
aa[++m]=a[++ct]=++o,bb[m]=b[ct]=0;
SA(ct,o),h(ct);
tot=(tot-ans+mod)%mod,ans=0;
ct=0;
for(int j=k;j>=1;j--) a[++ct]=c[j]-96,b[ct]=p[i];
a[++ct]=o,b[ct]=0;
SA(ct,o),h(ct);
tot=(tot+ans*fp(p[i],mod-2)%mod)%mod,ans=0;
}
for(int i=1;i<=m;i++) a[i]=aa[i],b[i]=bb[i];
SA(m,o),h(m);
tot=(tot+ans)%mod;
printf("%lld",tot);
}

浙公网安备 33010602011771号