正题
题目链接:https://www.luogu.com.cn/problem/P2336
题目大意
nnn个名字(每个名字两个串),mmm次点名,如果一次点名里是一个名字两个串中的子串该人就要答到。
对于每次点名求多少个人答到,每个名字求答到多少次。
解题思路
先考虑第一问,我们将所有串串在一起(中间加大数)然后跑出SASASA和所有的LCPLCPLCP。
然后对于每个点名串起始位置kkk我们寻找一个区间[l,r][l,r][l,r]使得LCP(k,i)≥len(i∈[l,r])LCP(k,i)\geq len(i\in[l,r])LCP(k,i)≥len(i∈[l,r])
这个区间我们求出heightheightheight后用STSTST表+二分可以求出
这样如果任意一个名字串的在这个区间内就会被点到。因为有名有姓,我们把同一个名字的SASASA染成同一种颜色,这样问题就变为了在[l,r][l,r][l,r]这个区间内有多少种颜色
而第二问也变为了有多少个区间包含该种颜色,我们直接用树状数组做就好了。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define lowbit(x) (x&-x)
using namespace std;
const int N=5e5+10;
struct node{int l,r,id;
}q[N];
int n,m,num,Q,s[N];
int col[N],len[N],head[N];
int sa[N],c[N],x[N],y[N];
int rk[N],height[N];
int st[N][20],lg[N];
int pre[N],last[N],qq[N];
int ans1[N],ans2[N];
struct Tree_Array{int t[N];void Change(int x,int val){if(!x) return;while(x<=n){t[x]+=val;x+=lowbit(x);}return;}int Ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}
}T1,T2;
void Init(){scanf("%d%d",&num,&Q);m=1e4;for(int i=1;i<=num;i++){for(int j=0;j<2;j++){int x,c;scanf("%d",&x);while(x--){scanf("%d",&c);s[++n]=c;col[n]=i;}s[++n]=++m;}}for(int i=1;i<=Q;i++){int x;head[n+1]=i;scanf("%d",&len[i]);for(int j=1;j<=len[i];j++){scanf("%d",&x);s[++n]=x;col[n]=-i;}s[++n]=++m;}
}
void Qsort(){for(int i=1;i<=m;i++)c[i]=0;for(int i=1;i<=n;i++)c[x[i]]++;for(int i=1;i<=m;i++)c[i]+=c[i-1];for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;return;
}
void Get_SA(){for(int i=1;i<=n;i++)x[i]=s[i],y[i]=i;Qsort();for(int w=1;w<=n;w<<=1){int 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;Qsort();swap(x,y);x[sa[1]]=p=1;for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:++p;if(p==n) break;m=p;}return;
}
void Get_Height(){int k=0;for(int i=1;i<=n;i++)rk[sa[i]]=i;for(int i=1;i<=n;i++){//if(rk[i]==1) continue;if(k) k--;int j=sa[rk[i]-1];while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;height[rk[i]]=k;}return;
}
void Get_ST(){lg[0]=-1;for(int i=1;i<=n;i++)st[i][0]=height[i],lg[i]=lg[i>>1]+1;for(int j=1;j<19;j++)for(int i=1;i+(1<<j-1)<=n;i++)st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);return;
}
int LCP(int l,int r){if(l==r) return 1e9+7;if(l>r) swap(l,r);l++;int z=lg[r-l+1];return min(st[l][z],st[r+1-(1<<z)][z]);
}
bool cmp(node x,node y)
{return x.r<y.r;}
void Get_Pre(){for(int i=1;i<=n;i++){if(col[sa[i]]>0){pre[i]=last[col[sa[i]]];last[col[sa[i]]]=i;}if(head[i]){int l=1,r=rk[i];while(l<=r){int mid=(l+r)>>1;if(LCP(mid,rk[i])>=len[head[i]]) r=mid-1;else l=mid+1;}q[head[i]].l=l;l=rk[i];r=n;while(l<=r){int mid=(l+r)>>1;if(LCP(rk[i],mid)>=len[head[i]]) l=mid+1;else r=mid-1;}q[head[i]].r=r;q[head[i]].id=head[i];qq[q[head[i]].l]++;}}sort(q+1,q+1+Q,cmp);
}
void Get_Ans(){int h=1,z=1;for(int i=1;i<=n;i++){T2.Change(i,qq[i]);if(col[sa[i]]>0){T1.Change(pre[i],-1);T1.Change(i,1);ans2[col[sa[i]]]+=T2.Ask(i)-T2.Ask(pre[i]);}while(h<=Q&&q[h].r<=i){ans1[q[h].id]+=T1.Ask(q[h].r)-T1.Ask(q[h].l-1);T2.Change(q[h].l,-1);h++;}}return;
}
int main()
{Init();Get_SA();Get_Height();Get_ST();Get_Pre();Get_Ans();for(int i=1;i<=Q;i++)printf("%d\n",ans1[i]);for(int i=1;i<=num;i++)printf("%d ",ans2[i]);
}