传送门
原题数据过水导致一个log跑不过根号
目前知道两种根号做法,暴跳SAM的根号做法和本身就是根号的莫队做法,如果要卡的话这两种是绝对不可能比log快的。来加一个强制在线
题解:
首先用map存转移把广义SAM建出来。
我们注意到第一个询问就是问终点的fail树内部有多少个不同的串。
直接树上差分打标记解决。
第二个询问就是问它的所有匹配点在fail树上到根的链的并集上有多少个之前的询问的终点。
还是树上差分。。。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
namespace Tree{
cs int N=4e5+7;
std::vector<int> G[N];
int fa[N],son[N],d[N];
int siz[N],top[N],in[N],dfn;
void dfs1(int u,int p){
siz[u]=1;fa[u]=p,d[u]=d[p]+1;
for(int re v:G[u]){
dfs1(v,u);siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp){
in[u]=++dfn;top[u]=tp;
for(int re v:G[u])
dfs2(v,v==son[u]?tp:v);
}
inline int LCA(int u,int v){
while(top[u]!=top[v])d[top[u]]<d[top[v]]?v=fa[top[v]]:u=fa[top[u]];
return d[u]<d[v]?u:v;
}
inline void init(){
dfs1(1,0);
dfs2(1,1);
}
}
namespace SAM{
cs int N=4e5+7;
std::map<int,int> son[N];
int fa[N],len[N],now=1;
inline int push_back(int p,int c){
auto it=son[p].find(c);
if(it!=son[p].end()){
if(len[it->second]==len[p]+1)return it->second;
else {
int nq=++now,q=it->second;
son[nq]=son[q],len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=nq;
for(;p&&(it=son[p].find(c),it->second==q);p=fa[p])it->second=nq;
return nq;
}
}
else {
int cur=++now;len[cur]=len[p]+1;
for(;p&&!son[p].count(c);p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=1;
else {
it=son[p].find(c);
if(len[it->second]==len[p]+1)fa[cur]=it->second;
else {
int nq=++now,q=it->second;
son[nq]=son[q],len[nq]=len[p]+1;
fa[nq]=fa[q],fa[cur]=fa[q]=nq;
for(;p&&(it=son[p].find(c),it->second==q);p=fa[p])it->second=nq;
}
}
return cur;
}
}
int bin[N],nd[N];
inline void build(){
for(int re i=1;i<=now;++i)++bin[len[i]];
for(int re i=1;i<=now;++i)bin[i]+=bin[i-1];
for(int re i=1;i<=now;++i)nd[bin[len[i]]--]=i;
for(int re i=2;i<=now;++i)Tree::G[fa[i]].push_back(i);
}
int tag[N],val[N];
inline void push_up(){
for(int re i=now;i;--i){
int u=nd[i];
val[u]+=tag[u],tag[u]=0;
val[fa[u]]+=val[u];
}
}
inline void push_down(){
val[1]=tag[1],tag[1]=0;val[0]=0;
for(int re i=2;i<=now;++i){
int u=nd[i];
val[u]=val[fa[u]]+tag[u];
tag[u]=0;
}
}
inline void query(){
int l=gi(),u=1,flag=true;
while(l--){
int c=gi();
if(flag){
auto it=son[u].find(c);
if(it==son[u].end())flag=false;
else u=it->second;
}
}
if(flag)++tag[u],cout<<val[u]<<"\n";
else cout<<0<<"\n";
}
}
cs int N=5e4+7,M=1e5+7;
int n,m;
int l1[N],l2[N];
int s[M<<1|1],ps[M<<1|1],lca[M<<1|1];
signed main(){
#ifdef zxyoi
freopen("meow.in","r",stdin);
#endif
n=gi(),m=gi();
for(int re i=1;i<=n;++i){
l1[i]=gi()+l2[i-1];
for(int re j=l2[i-1]+1,p=1;j<=l1[i];++j)
ps[j]=p=SAM::push_back(p,s[j]=gi());
l2[i]=gi()+l1[i];
for(int re j=l1[i]+1,p=1;j<=l2[i];++j)
ps[j]=p=SAM::push_back(p,s[j]=gi());
}
SAM::build();Tree::init();
for(int re i=1;i<=n;++i){
std::sort(ps+l2[i-1]+1,ps+l2[i]+1,[](int u,int v){
return Tree::in[u]<Tree::in[v];
});
}
for(int re i=1;i<=n;++i){
for(int re j=l2[i-1]+2;j<=l2[i];++j)
lca[j]=Tree::LCA(ps[j-1],ps[j]);
}
for(int re i=1;i<=l2[n];++i)SAM::tag[ps[i]]++,SAM::tag[lca[i]]--;
SAM::push_up();while(m--)SAM::query();SAM::push_down();
for(int re i=1;i<=n;++i){
int ans=0;
for(int re j=l2[i-1]+1;j<=l2[i];++j)
ans+=SAM::val[ps[j]]-SAM::val[lca[j]];
cout<<ans<<" ";
}
return 0;
}