NFLSOJ #12404. -「NOIP2021模拟赛0904长郡」 王总歌声(SAM+根分+虚树)
现场想到正解了,可惜数组开小了 WA 80 >_<
其实是个歪解哦,复杂度比 std 更优可不知道为什么跑得比 std 慢
现场上来把题看错了,一开始看成是 \(S_i\) 要是 \(T\) 的子串,然后心想这不一遍 AC 自动机 + 虚树不就完事了吗,然鹅写好之后测了下样例才发现不对劲……
在上面我看错题目的版本中,\(T\) 作为文本串出现,这天然地构成了一个多模式串匹配的模型,因此可以使用 AC 自动机求解。但在这个版本中这样做就行不通了。注意到这里涉及子串,又不是多模式匹配的模型,因此考虑 SA/SAM。注意到题目中的 \(S_i\) 是不会变的,因此可以预处理的时候就对 \(S_i\) 把广义 SAM 建出来。这样每次询问时,我们就尝试用 \(T\) 在 SAM 上匹配,如果发现走到某个地方走不通了那显然答案是 \(0\),否则我们可以在 \(\mathcal O(|T|)\) 时间内找到 \(T\) 在 SAM 上表示的节点 \(x\)。根据 edp 那套理论,包含 \(T\) 作为结束的前缀就是 \(T\) 在 parent tree 子树内所有结束问题,这样一来一个字符串 \(S_i\) 会对 \(T\) 产生 \(C_i\) 的贡献当且仅当 \(\exists S_i\) 的某个前缀,满足其表示的节点在 \(x\) 的子树内,因此我们可以将原问题规约到这样的情况:
给你一棵树,有一些节点有颜色,每个颜色有一个权值,你要支持将颜色权值 \(+v\),或者求子树内所有出现过的权值之和。
这东西看起来有点像二维数点,但每次将颜色权值 \(+v\) 会涉及到多个点,因此复杂度会退化。注意到这样涉及到对一个下标集合中所有元素位置上的值 \(+v\) 这样的操作,一般可以根号分治。因此考虑根分,对于长度 \(>\sqrt{\sum\limits_{i=1}^n|S_i|}=B\) 的字符串直接将它们的权值相加,然后查询的时候暴力统计它们的贡献即可。对于长度 \(\le B\) 的字符串稍微有点繁琐,不过注意到这东西可以写成,将区间上 \(B\) 个点到根节点路径并上所有的答案 \(+v\),这东西看起来可以虚树,具体来说将这个点集所有元素按 DFS 序排个序形成一个圆环,然后每次将圆环上相邻两个点路径上权值 \(+v\),最后额外将所有点 LCA 到根节点路径权值 \(+v\)。这样直接每次询问排个序复杂度是 \(n\sqrt{n}\log n\) 的,不过注意到上面的操作可以写成进行一系列形如“将 \(u\) 到根节点路径上所有点权值 \(+tv\)”的形式,其中 \(t=\pm 1\),因此每次将这个操作序列保存下来,每次遇到长度 \(\le B\) 的字符串就暴力对所有操作序列中的元素执行相应的 \(+v/-v\) 的操作。然后 DFS 序 + 一个能够支持单点加、区间求和的数据结构即可回答询问。使用树状数组还是多一个 \(\log\),不过注意到单点加次数会达到 \(n\sqrt{n}\),而查询只有 \(\mathcal O(n)\),因此根据根号平衡的思想可以采取分块,这样即可 \(\mathcal O(1)\) 单点加 \(\mathcal O(\sqrt{n})\) 求和,总复杂度就是 \(n\sqrt{n}\)
const int MAXN=3e5;
const int MAXP=1e6;
const int LOG_N=20;
const int B=255;
const int SQRT=300000>>8;
int n,qu,sm;
struct node{int ch[26],lnk,len;} s[MAXP+5];
int ncnt=1,cur=1;
void extend(char c){
int id=c-'a',nw=++ncnt,p=cur;
s[nw].len=s[cur].len+1;cur=nw;
while(p&&!s[p].ch[id]) s[p].ch[id]=nw,p=s[p].lnk;
if(!p) return s[nw].lnk=1,void();
int q=s[p].ch[id];
if(s[q].len==s[p].len+1) return s[nw].lnk=q,void();
int cl=++ncnt;s[cl].len=s[p].len+1;
s[cl].lnk=s[q].lnk;s[q].lnk=s[nw].lnk=cl;
for(int i=0;i<26;i++) s[cl].ch[i]=s[q].ch[i];
while(p&&s[p].ch[id]==q) s[p].ch[id]=cl,p=s[p].lnk;
}
ll c[MAXN+5];
vector<int> pt[MAXN+5];
vector<pii> opt[MAXN+5];
int hd[MAXP+5],nxt[MAXP+5],to[MAXP+5],ec=0;
void adde(int u,int v){/*printf("%d %d\n",u,v);*/to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int bgt[MAXP+5],edt[MAXP+5],fa[MAXP+5][LOG_N+2],dep[MAXP+5],tim=0;
void dfs(int x,int f){
bgt[x]=++tim;fa[x][0]=f;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];dep[y]=dep[x]+1;
dfs(y,x);
} edt[x]=tim;
}
int getlca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=LOG_N;~i;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=LOG_N;~i;i--) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
assert(fa[x][0]!=0);
return fa[x][0];
}
int lrg[MAXN+5],lrg_cnt=0,len[MAXN+5];
int blk_sz,blk_cnt,bel[MAXP+5],L[MAXN+5],R[MAXN+5];
ll val[MAXP+5],blk_sm[MAXP+5];
bitset<MAXP+5> vis[SQRT+5];
void add(int p,ll x){/*printf("add %d %d\n",p,x);*/val[p]+=x;blk_sm[bel[p]]+=x;}
ll query(int l,int r){
// printf("query %d %d\n",l,r);
ll res=0;
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++) res+=val[i];
} else {
for(int i=l;i<=R[bel[l]];i++) res+=val[i];
for(int i=L[bel[r]];i<=r;i++) res+=val[i];
for(int i=bel[l]+1;i<bel[r];i++) res+=blk_sm[i];
} return res;
}
void insert(int x,ll v){
if(len[x]>B) c[x]+=v;
else for(pii p:opt[x]) add(p.fi,p.se*v);
}
int main(){
freopen("song.in","r",stdin);
freopen("song.out","w",stdout);
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++){
scanf("%lld",&c[i]);string str;cin>>str;sm+=str.size();
cur=1;for(int j=0;j<str.size();j++) extend(str[j])/*,printf("%d\n",cur)*/,pt[i].pb(cur);
len[i]=str.size();//printf("%d\n",pt[i].size());
} for(int i=2;i<=ncnt;i++) adde(s[i].lnk,i);dfs(1,0);
// for(int i=1;i<=n;i++){
// printf("pt %d: ",i);
// for(int x:pt[i]) printf("%d ",x);
// printf("\n");
// }
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=ncnt;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
// for(int i=1;i<=ncnt;i++) printf("%d %d %d\n",bgt[i],edt[i],s[i].len);
for(int i=1;i<=n;i++){
sort(pt[i].begin(),pt[i].end(),[&](int x,int y){return bgt[x]<bgt[y];});
for(int j=0;j<pt[i].size();j++) opt[i].pb(mp(bgt[pt[i][j]],2));
for(int j=0;j+1<pt[i].size();j++) opt[i].pb(mp(bgt[getlca(pt[i][j],pt[i][j+1])],-2));
} blk_sz=(int)sqrt(ncnt);blk_cnt=(ncnt-1)/blk_sz+1;
for(int i=1;i<=blk_cnt;i++){
L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,ncnt);
for(int j=L[i];j<=R[i];j++) bel[j]=i;
}
for(int i=1;i<=n;i++){
if(len[i]>B) lrg[++lrg_cnt]=i;
else insert(i,c[i]);
}
for(int i=1;i<=lrg_cnt;i++){
for(int x:pt[lrg[i]]){
int cur=x;
while(1){
if(vis[i][cur]||!cur) break;
vis[i][cur]=1;cur=s[cur].lnk;
}
}
}
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){
string str;int cc=1;bool flg=1;cin>>str;
for(int i=0;i<str.size();i++){
if(!s[cc].ch[str[i]-'a']){flg=0;break;}
cc=s[cc].ch[str[i]-'a'];
}
if(!flg){puts("0");continue;}
else{
ll res=0;
for(int i=1;i<=lrg_cnt;i++) if(vis[i][cc]) res+=c[lrg[i]]*2;
res+=query(bgt[cc],edt[cc]);
printf("%lld\n",res>>1);
}
} else {
int x,y;scanf("%d%d",&x,&y);
insert(x,y);
}
}
return 0;
}
/*
3 3
3 abab
2 baba
1 abba
1 ba
2 1 2
1 aba
*/

浙公网安备 33010602011771号