[JSOI2015]字符串树

可持久化 trie

链接:[P6088 JSOI2015]字符串树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:给定一棵树,边权为字符串,多次询问路径 u 到 v 经过的所有边的所有字符串有多少个前缀是 w。

题解:可持久化字典树维护,每次求 u , v u,v uv l c a lca lca,然后分两段查询相加即可。

//#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")
#include
#include
#include
#include

using namespace std;
typedef pair<int,string> P;
const int maxn=1e5+5;

string w;
int n,m,u,v;
int dep[maxn],lg[maxn],fa[maxn][18];
int p[maxn*10][26],rt[maxn],ct[maxn*10],now;
vector<P>ed[maxn];

void insert(int u,int v,string s)
{
	rt[v]=++now; 
	int l=rt[u],r=rt[v]; ct[r]=ct[l]+1;
	for(int i=0;i<s.length();i++)
	{
		int q=s[i]-'a';
		for(int j=0;j<=25;j++)p[r][j]=p[l][j];
		p[r][q]=++now,l=p[l][q],r=p[r][q],ct[r]=ct[l]+1;
	}
	return;
}

int query(int u,int v,string s)
{
	int l=rt[u],r=rt[v];
	for(int i=0;i<s.length();i++)
	{
		l=p[l][s[i]-'a'],r=p[r][s[i]-'a'];
	}
	return ct[r]-ct[l];	
}

int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	while(dep[x]>dep[y])
	{
		x=fa[x][lg[dep[x]-dep[y]]-1];
	}
	if(x==y)return x;
	for(int i=17;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i],y=fa[y][i];
		}
	}
	return fa[x][0];
}

void dfs(int x,int f,string s)
{
	dep[x]=dep[f]+1,fa[x][0]=f;
	if(s.length())insert(f,x,s);
	for(int i=1;i<=17;i++)
	{
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(auto y:ed[x])
	{
		if(y.first!=f)dfs(y.first,x,y.second);
	}
	return;
}

int Query(int x,int y,string s)
{
	int lca=LCA(x,y);
	return query(lca,x,s)+query(lca,y,s);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)lg[i]=lg[i-1]+(!(i&i-1));
	for(int i=1;i<n;i++)
	{
		cin>>u>>v>>w;
		ed[u].push_back(P(v,w));
		ed[v].push_back(P(u,w)); 
	}
	dfs(1,0,""),cin>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v>>w;
		cout<<Query(u,v,w)<<"\n";
	}
	return 0;
}

你可能感兴趣的:(主席树,深度优先,算法,数据结构,c++)