标签:后缀 i+1 种类 不同 operator 现在 tps 推荐 ||
这题tm把AC自动机叉掉了,,,
只能考虑别的做法
把所有串连在一起,不同串的交界处加入一些不同的字符,然后求出sa数组和height数组,现在一个询问的答案就是和那个询问串的lcp正好为询问串长度的原串个数,而这在把后缀排好序后是一个区间,每个原串答案为包含这个原串的某个点的区间个数
如果将每个原串的点染成对应编号的颜色,那么现在求的是每个区间包含的点的颜色种类和每种点被多少个区间包含,可以用莫队实现,前者就是HH的项链,后者可以在每种颜色对第一种答案加上贡献时对其答案加上剩余询问个数,在对第一种答案减去贡献时对其答案减去剩余询问个数
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
using namespace std;
const int N=400000+10,M=100000+10;
il int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int n,l1,l2,m=10000;
int rk[N],sa[N],wr[N],bk[N],wx[N],he[N];
int cc[N];
il bool cmp(int a,int b,int j){return wr[a]==wr[b]&&wr[a+j]==wr[b+j];}
il void gSA(int m)
{
for(int i=1;i<=n;++i) ++bk[rk[i]=cc[i]];
for(int i=1;i<=m;++i) bk[i]+=bk[i-1];
for(int i=n;i;--i) sa[bk[rk[i]]--]=i;
int tp,j=1,p=0;
while(p<n)
{
tp=0;
for(int i=n-j+1;i<=n;++i) wx[++tp]=i;
for(int i=1;i<=n;++i) if(sa[i]>j) wx[++tp]=sa[i]-j;
for(int i=1;i<=n;++i) wr[i]=rk[wx[i]];
for(int i=0;i<=m;++i) bk[i]=0;
for(int i=1;i<=n;++i) ++bk[wr[i]];
for(int i=1;i<=m;++i) bk[i]+=bk[i-1];
for(int i=n;i;--i) sa[bk[wr[i]]--]=wx[i];
for(int i=1;i<=n;++i) wr[i]=rk[i];
rk[sa[1]]=p=1;
for(int i=2;i<=n;++i) rk[sa[i]]=p+=(cmp(sa[i],sa[i-1],j)^1);
m=p,j<<=1;
}
for(int i=1,j;i<=n;++i)
{
he[rk[i]]=he[rk[i-1]]?he[rk[i-1]]-1:0;
j=sa[rk[i]-1];
while(cc[i+he[rk[i]]]==cc[j+he[rk[i]]]) ++he[rk[i]];
}
}
int p[M][2],w[M],len[M],be[N],b2[N],cn[M],na,a1[M],a2[M];
il void ad(int x,int res)
{
++cn[b2[sa[x]]];
if(b2[sa[x]]&&cn[b2[sa[x]]]==1) ++na,a2[b2[sa[x]]]+=res;
}
il void dl(int x,int res)
{
--cn[b2[sa[x]]];
if(b2[sa[x]]&&cn[b2[sa[x]]]==0) --na,a2[b2[sa[x]]]-=res;
}
int mi[N][20],hbt[N];
il int gmin(int l,int r)
{
if(l>r) swap(l,r);
++l;
if(l>r) return -1;
int z=hbt[r-l+1];
return min(mi[l][z],mi[r-(1<<z)+1][z]);
}
struct qu
{
int l,r,id;
bool operator < (const qu bb) const {return (be[l]^be[bb.l])?l<bb.l:r<bb.r;}
}qq[M];
int main()
{
l1=rd(),l2=rd();
cc[0]=m;
for(int i=1;i<=l1;++i)
{
p[i][0]=n+1;
int k=rd();
while(k--) cc[++n]=rd();
cc[++n]=++m;
k=rd();
while(k--) cc[++n]=rd();
cc[++n]=++m;
p[i][1]=n-1;
}
for(int i=1;i<=l2;++i)
{
w[i]=n+1;
int k=len[i]=rd();
while(k--) cc[++n]=rd();
cc[++n]=++m;
}
--n;
gSA(m);
int sqrtn=sqrt(n),lz=log2(n);
hbt[0]=-1;
for(int i=0;i<=n;++i) be[i]=i/sqrtn;
for(int i=1;i<=n;++i) hbt[i]=hbt[i-1]+((i&(-i))==i);
for(int i=1;i<=n;++i)
mi[i][0]=he[i];
for(int j=1;j<=lz;++j)
for(int i=1;i<=n-(1<<(j-1));++i)
mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
for(int i=1;i<=l2;++i)
{
qq[i].l=qq[i].r=rk[w[i]],qq[i].id=i;
int l=1,r=rk[w[i]]-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(gmin(mid,rk[w[i]])==len[i]) qq[i].l=mid,r=mid-1;
else l=mid+1;
}
l=rk[w[i]]+1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(gmin(rk[w[i]],mid)==len[i]) qq[i].r=mid,l=mid+1;
else r=mid-1;
}
}
sort(qq+1,qq+l2+1);
for(int i=1,j=1;;++i)
{
while(j<=l1&&i>p[j][1]) ++j;
if(j>l1) break;
b2[i]=(i>=p[j][0]?j:0);
}
for(int i=1,l=1,r=0;i<=l2;++i)
{
while(r<qq[i].r) ++r,ad(r,l2-i+1);
while(r>qq[i].r) dl(r,l2-i+1),--r;
while(l<qq[i].l) dl(l,l2-i+1),++l;
while(l>qq[i].l) --l,ad(l,l2-i+1);
a1[qq[i].id]=na;
}
for(int i=1;i<=l2;++i) printf("%d\n",a1[i]);
for(int i=1;i<=l1;++i) printf("%d ",a2[i]);
return 0;
}
推荐去dd我的xzz巨佬博客看更优秀的题解
标签:后缀 i+1 种类 不同 operator 现在 tps 推荐 ||
原文地址:https://www.cnblogs.com/smyjr/p/10100311.html