【题解】[SCOI2012]喵星球上的点名
\(\text{Solution:}\)
第一问很简单,直接树状数组实现子树数颜色就好了。问题在第二问。
第二问的感觉不是很好,无从下手。第一个想法是考虑是不是可以用线段树合并来做这件事,但感觉会很麻烦,并没有想清楚怎么做。
参考题解区的做法也没有详细讲解,这里根据自己的理解讲一下正确性,代码复杂度还有待商榷。据说期望是根号的。
考虑把所有串拎出来一个个求答案。从开头到结尾依次找到其在 parent 树上对应的节点,然后从这个节点不断向上跳父亲。
把沿途点的所有答案都累加上。这为什么是对的?
我们发现,parent 树上的父亲代表的串必然是其孩子的后缀。也就是说,如果我们找到了一个串的前缀对应的节点,那么这个节点在 parent 树上一直到根的答案都是需要累积的。
而且这样做显然是不会重复的。因为每一个记录的串结尾不同以及长度不同。
为了保证复杂度,每次直接对找过的点打标记。因为这个点向上的所有点其实都已经被计算过了。
于是依次遍历串去更新答案即可。复杂度证明暂时没有找到。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,tr[N];
inline int lowbit(int x){return x&(-x);}
void change(int x,int v,int TT){while(x<=TT)tr[x]+=v,x+=lowbit(x);}
int query(int x){
if(!x)return 0;
int res=0;
while(x){
res+=tr[x];
x-=lowbit(x);
}
return res;
}
namespace SAM{
int len[N],pa[N],tot=1,last=1,col[N],siz[N];
int dfstime,idfn[N],dfn[N],cpos[N],num[N];
int pos[N],cdfn[N],cnt[N],Ansnum,Ans[N];
map<int,int>ch[N];
vector<int>G[N],qr[N];
void insert(const int &c,const int &cl){
int p=last;int np=++tot;last=tot;
col[np]=cl;
for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
if(!p)pa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)pa[np]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;
pa[nq]=pa[q];pa[q]=pa[np]=nq;
ch[nq]=ch[q];
for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
}
}
}
void dfs(int x){
idfn[dfn[x]=++dfstime]=x;
cdfn[dfstime]=col[x];
siz[x]=1;
for(auto v:G[x]){
dfs(v);
siz[x]+=siz[v];
}
}
void Build(){
for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
dfs(1);
for(int i=1;i<=tot;++i)
qr[dfn[i]+siz[i]-1].push_back(i);
for(int i=1;i<=tot;++i){
if(!cpos[cdfn[i]])cpos[cdfn[i]]=i;
pos[i]=cpos[cdfn[i]];
cpos[cdfn[i]]=i;
}
for(int i=2;i<=tot;++i){
if(cdfn[i]){
change(i,1,tot);
if(pos[i]!=i)change(pos[i],-1,tot);
}
for(auto v:qr[i]){
num[v]=query(i)-query(dfn[v]-1);
}
}
}
void dfsans(int x){
Ansnum+=cnt[x];
int numx=0;
for(auto v:G[x]){
numx++;
dfsans(v);
}
if(!numx)Ans[col[x]]+=Ansnum;
Ansnum-=cnt[x];
}
}
using namespace SAM;
int scnt,t[N<<1];
int lens[N][2];
inline void update(int x,int y){
for(;x&&cpos[x]!=y;x=pa[x]){
cpos[x]=y;
Ans[y]+=cnt[x];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
int lent;
last=1;
scanf("%d",&lent);
lens[i][0]=lent;
for(int j=1;j<=lent;++j){
int x;
scanf("%d",&x);
t[++scnt]=x;
insert(x,i);
}
last=1;
scanf("%d",&lent);
lens[i][1]=lent;
for(int j=1;j<=lent;++j){
int x;
scanf("%d",&x);
t[++scnt]=x;
insert(x,i);
}
}
Build();
// for(int i=1;i<=tot;++i)printf("%d ",num[i]);
// puts("");
// for(int i=1;i<=tot;++i)printf("%d ",col[i]);
// puts("");
// for(int i=1;i<=tot;++i)printf("%d ",cdfn[i]);
// puts("");
// for(int i=1;i<=tot;++i)printf("%d ",col[idfn[i]]);
// puts("");
// for(int i=1;i<=tot;++i)printf("(%d %d)\n",cpos[cdfn[i]],pos[i]);
for(int qq=1;qq<=m;++qq){
int lent;
scanf("%d",&lent);
int now=1,fg=0;;
for(int i=1;i<=lent;++i){
int v;
scanf("%d",&v);
now=ch[now][v];
if(!now)fg=1;
}
if(fg)puts("0");
else printf("%d\n",num[now]),cnt[now]++;
}
int snum=0;
for(int i=1;i<=tot;++i)cpos[i]=0;
for(int i=1;i<=n;++i){
int now=1;
for(int j=1;j<=lens[i][0];++j)now=ch[now][t[++snum]],update(now,i);
now=1;
for(int j=1;j<=lens[i][1];++j)now=ch[now][t[++snum]],update(now,i);
}
for(int i=1;i<=n;++i)printf("%d ",Ans[i]);
return 0;
}