NOIp训练 子串查找VII(树剖+ac自动机+dfs序+bit套主席树)

传送门
题意:
有一棵 n n n个点的无根树,第 i i i个点上有一个字符串 s i s_i si作为编号和一个权值 v i v_i vi,现在有 m m m次强制在线的询问/修改。
询问:给出一个字符串 S S S和一条路径 ( u , v ) (u,v) (u,v),路径上 i i i点的贡献是 s i s_i si S S S中出现次数* v i v_i vi,问总贡献。
时间复杂度: O ( n l o g 3 n ) , ∑ ∣ s i ∣ , ∑ ∣ S ∣ , n , m O(nlog^3n),\sum|s_i|,\sum|S|,n,m O(nlog3n)si,S,n,m同阶
修改:修改 v i v_i vi
思路:
先把原树树剖了并求出 d f s dfs dfs序。
然后拿出所有的 s i s_i si a c ac ac自动机,然后求出 f a i l fail fail树的 d f s dfs dfs序。
考虑没有修改如何维护答案。
先利用树剖每条重链上 d f s dfs dfs序连续的性质分成 l o g log log次询问。
设这次询问允许出现的点的 d f s dfs dfs序编号区间是 [ q l , q r ] [ql,qr] [ql,qr]
我们拿 S S S a c ac ac自动机上面匹配,假设每次加一个字符之后转移到 p p p点,那么这一次的总贡献只跟 f a i l fail fail树上从 p p p到根节点路径上所有点有关。
考虑对于路径上每个点如何计算贡献:
限制:

  1. 如果这个点必须是至少一个串的结尾字符。
  2. 如果这个点对应原树上 d f s dfs dfs编号必须在区间 [ q l , q r ] [ql,qr] [ql,qr]中。

对答案的贡献: v i v_i vi

思考一下如何同时维护一条链上面的答案。
我们可以对 f a i l fail fail树建一棵主席树,叶子 i i i记录原树 d f s dfs dfs序编号为 i i i的贡献,然后就变成了区间求和问题。

带修怎么做???
发现就是对子树里的所有主席树进行修改,用 b i t bit bit套主席树即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
typedef long long ll;
typedef pair<int,ll> pii;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
inline ll readl(){
	ll ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
inline vector<char>Read(){
	vector<char>S;
	S.push_back('0');
	char ch=gc();
	while(!isalpha(ch))ch=gc();
	while(isalpha(ch))S.push_back(ch),ch=gc();
	return S;
}
const int N=4e5+5,M=2e5+5;
inline int idx(char x){
	switch(x){
		case 'A':return 0;break;
		case 'T':return 1;break;
		case 'C':return 2;break;
		case 'G':return 3;break;
		default:return 4;break;
	}
}
vector<char>s[M],t;
int rt[N],n,m,Tot=0;
ll a[M];
namespace sgt{
	#define lc (son[p][0])
	#define rc (son[p][1])
	#define mid (l+r>>1)
	int son[M*100][2],tot=0;
	ll sum[M*100];
	inline int lowbit(const int&x){return x&-x;}
	inline void update(int&p,int o,int l,int r,int k,ll v){
		if(!p)p=++tot;
		sum[p]=sum[o]+v,lc=son[o][0],rc=son[o][1];
		if(l==r)return;
		k<=mid?update(lc,lc,l,mid,k,v):update(rc,rc,mid+1,r,k,v);
	}
	inline ll query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return sum[p];
		if(qr<=mid)return query(lc,l,mid,ql,qr);
		if(ql>mid)return query(rc,mid+1,r,ql,qr);
		return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
	}
	inline ll query(int p,int ql,int qr){ll ret=0;for(p;p;p^=lowbit(p))ret+=query(rt[p],1,n,ql,qr);return ret;}
	inline void update(int p,pii a,int ff){for(p;p<=Tot+1;p+=lowbit(p))update(rt[p],rt[p],1,n,a.fi,a.se*ff);}
	inline void update(int ql,int qr,pii a){update(ql,a,1),update(qr+1,a,-1);}
	#undef lc
	#undef rc
	#undef mid
}
namespace acam{
	int in[N],out[N],fail[N],tot=0,son[N][5],pos[M];
	vector<int>e[N];
	vector<pii>val[N];
	inline void insert(pii a,vector<char>s){
		ri p=0;
		for(ri x,i=1,up=s.size()-1;i<=up;++i){
			if(!son[p][x=idx(s[i])])son[p][x]=++Tot;
			p=son[p][x];
		}
		pos[a.fi]=p;
		val[p].push_back(a);
	}
	inline void getfail(){
		static int q[N],hd,tl;
		hd=1,tl=0;
		for(ri i=0;i<5;++i)if(son[0][i])fail[q[++tl]=son[0][i]]=0;
		while(hd<=tl){
			int p=q[hd++];
			for(ri i=0,v;i<5;++i){
				if(!son[p][i]){son[p][i]=son[fail[p]][i];continue;}
				fail[son[p][i]]=son[fail[p]][i];
				q[++tl]=son[p][i];
			}
			e[fail[p]].push_back(p);
		}
	}
	inline ll query(int ql,int qr,vector<char>s){
		ll ret=0;
		for(ri p=0,i=1,up=s.size()-1;i<=up;++i){
			p=son[p][idx(s[i])];
			ret+=sgt::query(in[p],ql,qr);
		}
		return ret;
	}
	inline void update(pii x){
		int p=pos[x.fi];
		sgt::update(in[p],out[p],x);
	}
	void dfs(int p){
		in[p]=++tot;
		for(ri i=0;i<e[p].size();++i)dfs(e[p][i]);
		out[p]=tot;
		for(ri i=0;i<val[p].size();++i)sgt::update(in[p],out[p],val[p][i]);
	}
	inline void init(){
		getfail();
		dfs(0);
	}
}
namespace Tree{
	int siz[M],hson[M],dep[M],top[M],fa[M],tot=0,num[M];
	vector<int>e[M];
	void dfs1(int p){
		siz[p]=1;
		for(ri i=0,v;i<e[p].size();++i){
			if((v=e[p][i])==fa[p])continue;
			fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
			if(siz[v]>siz[hson[p]])hson[p]=v;
		}
	}
	void dfs2(int p,int tp){
		top[p]=tp,num[p]=++tot;
		acam::insert(pii(num[p],a[p]),s[p]);
		if(!hson[p])return;
		dfs2(hson[p],tp);
		for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa[p]&&v!=hson[p])dfs2(v,v);
	}
	inline ll query(int x,int y,vector<char>s){
		ll ret=0;
		while(top[x]^top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			ret+=acam::query(num[top[x]],num[x],s);
			x=fa[top[x]];
		}
		if(dep[x]<dep[y])swap(x,y);
		ret+=acam::query(num[y],num[x],s);
		return ret;
	}
	inline void update(int p,ll v){
		acam::update(pii(num[p],v-a[p]));
		a[p]=v;
	}
}
inline void init(){
	Tree::dfs1(1);
	Tree::dfs2(1,1);
	acam::init();
}
ll opt,lastans=0;
int main(){
	n=read(),opt=read();
	for(ri i=1;i<=n;++i)s[i]=Read();
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),Tree::e[u].push_back(v),Tree::e[v].push_back(u);
	init();
	for(ri tt=read(),x,y,op;tt;--tt){
		op=read();
		switch(op){
			case 1:{
				x=readl()^(lastans*opt),y=readl()^(lastans*opt);
				t=Read();
				cout<<(lastans=Tree::query(x,y,t))<<'\n';
				break;
			}
			case 2:{
				x=readl()^(lastans*opt),y=readl()^(lastans*opt);
				Tree::update(x,y);
				break;
			}
		}
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值