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号
浙公网安备 33010602011771号