阿块刚才突然问我有什么歌听,那就推一首我正在听的歌吧:終わりの世界から。大魔王麻枝准的歌,歌词直接讲了个故事。
这种前缀关系一看就是上$trie$树了。
如果知道集合$G$,答案就随便求了。记$f(n)$为区间$[1,n]$的子区间个数,$f(n)=\dfrac{n(n+1)}{2}$。把集合元素排序,用所有方案减去不合法方案就是答案,即$f(\max\{|s_i|\})-\sum\limits_{i=1}^{n+1}f(a_i-a_{i-1}-1)$,其中$a_0=0,a_{n+1}=\max\{|s_i|\}+1$
求$G$有一个暴力做法:用set
维护$G$,把每个前缀代表的节点加上对应串的权值,如果某个节点满足要求就把对应的$g_{len(S)}$扔进set
里,扫一遍就是答案。
这个做法可以用莫队优化,但是移动一次端点要把整个串都算一遍,复杂度不对劲。套路地把每个字符都从串里拆出来,拼成一个新串,在新串上跑莫队。这样单次转移是set
的$O(\log n)$。
考虑去掉set
的$\log$。如果换成链表,可以$O(1)$删除,但难添加,于是可以上回滚莫队。复杂度$O(m\sqrt {\sum|s_i|})$。
第一次写只删除的回滚莫队,细节有点多,代码丑的一批:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 300005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
#define son(x,y) son[x][y]
int son[maxn][26],len[maxn],pos[maxn],id[maxn],v[maxn],deep[maxn],g[maxn],be[maxn],a[maxn],sta[maxn][3],cnt=1,A,B,C,k,ml,top;
long long ans[maxn];
bool vis[maxn],flag;
char s[maxn];
struct query{
int l,r,id;
bool operator < (const query &x)const{
if(be[l]!=be[x.l])return be[l]<be[x.l];
return r>x.r;
}
}q[maxn];
void insert(int l,int r){
int node=1;
for(register int i=l;i<=r;++i){
if(!son(node,s[i]-'a'))deep[son(node,s[i]-'a')=++cnt]=deep[node]+1;
node=son(node,s[i]-'a'),pos[i]=node;
}
}
inline long long sec(int n){return 1ll*n*(n-1)>>1;}
struct Mo{
int tax[maxn],pre[maxn],nex[maxn];
long long sum[maxn],ans;
bool vis[maxn];
inline void erase(int x){
ans-=sec(x-pre[x])+sec(nex[x]-x)-sec(nex[x]-pre[x]);
pre[nex[x]]=pre[x],nex[pre[x]]=nex[x];
if(flag)sta[++top][0]=x,sta[top][1]=pre[x],sta[top][2]=nex[x];
}
void add(int x){
int node=pos[x];
sum[node]+=v[x];
if(!vis[node]&&sum[node]*B+1ll*A*deep[node]>=C){
vis[node]=1;
if(!tax[deep[node]])a[++k]=g[deep[node]];
++tax[deep[node]];
}
}
inline void del(int x){
int node=pos[x];
sum[node]-=v[x];
if(vis[node]&&(!sum[node]||sum[node]*B+1ll*A*deep[node]<C)){
vis[node]=0,--tax[deep[node]];
if(!tax[deep[node]])erase(g[deep[node]]);
}
}
inline void back(int x){
int node=pos[x];
sum[node]+=v[x];
if(!vis[node]&&sum[node]*B+1ll*A*deep[node]>=C)vis[node]=1,++tax[deep[node]];
}
inline void back(){
int x,l,r;
while(top){
x=sta[top][0],l=sta[top][1],r=sta[top][2];
ans-=sec(r-l)-sec(x-l)-sec(r-x);
nex[x]=r,pre[x]=l,pre[r]=x,nex[l]=x,--top;
}
}
void init(){
sort(a+1,a+1+k);
pre[a[k+1]=ml+1]=a[k],nex[0]=a[1];
for(register int i=1;i<=k;++i){
pre[a[i]]=a[i-1],nex[a[i]]=a[i+1];
ans+=sec(a[i]-a[i-1]);
}
ans+=sec(a[k+1]-a[k]);
}
}o,u;
long long gcd(long long a,long long b){return b?gcd(b,a%b):a;}
int main(){
int m,sq,sl,n=read();
A=read(),B=read(),C=read();
for(register int i=1;i<=n;++i)a[i]=read();
for(register int i=1;i<=n;++i){
scanf("%s",s+len[i-1]+1),len[i]=strlen(s+len[i-1]+1),ml=max(ml,len[i]),len[i]+=len[i-1],insert(len[i-1]+1,len[i]);
for(register int j=len[i-1]+1;j<=len[i];++j)id[j]=i,v[j]=a[i];
}
for(register int i=1;i<=ml;++i)g[i]=read();
n=len[n],m=read(),sq=n/sqrt(m),sl=n/sq+bool(n%sq);
for(register int i=1;i<=m;++i)q[i].l=len[read()-1]+1,q[i].r=len[read()],q[i].id=i;
for(register int i=1;i<=sl;++i)
for(register int j=(i-1)*sq+1,r=min(j+sq-1,n);j<=r;++j)
be[j]=i;
sort(q+1,q+1+m);
for(register int i=1;i<=n;++i)o.add(i);
o.init();
int l,r;
for(register int i=1;i<=m;++i){
if(be[q[i].l]!=be[q[i-1].l]){
l=(be[q[i].l]-1)*sq+1,r=n;
for(register int j=max((be[q[i-1].l]-1)*sq+1,1);j<l;++j)o.del(j);
u=o;
}
while(r>q[i].r)u.del(r--);
flag=1;
for(register int j=l;j<q[i].l;++j)u.del(j);
flag=0;
ans[q[i].id]=u.ans;
for(register int j=l;j<q[i].l;++j)u.back(j);
u.back();
}
long long f=sec(ml+1);
for(register int i=1;i<=m;++i){
long long d=gcd(f-ans[i],f);
printf("%lld/%lld\n",(f-ans[i])/d,f/d);
}
}