zoukankan      html  css  js  c++  java
  • 【BZOJ4231】回忆树 离线+fail树+KMP

    【BZOJ4231】回忆树

    Description

    回忆树是树。
    具体来说,是n个点n-1条边的无向连通图,点标号为1~n,每条边上有一个字符(出于简化目的,我们认为只有小写字母)。
    对一棵回忆树来说,回忆当然是少不了的。
    一次回忆是这样的:你想起过往,触及心底…唔,不对,我们要说题目。
    这题中我们认为回忆是这样的:给定2个点u,v(u可能等于v)和一个非空字符串s,问从u到v的简单路径上的所有边按照到u的距离从小到大的顺序排列后,边上的字符依次拼接形成的字符串中给定的串s出现了多少次。

    Input

    第一行2个整数,依次为树中点的个数n和回忆的次数m。
    接下来n-1行,每行2个整数u、v和1个小写字母c,表示回忆树的点u、v之间有一条边,边上的字符为c
    接下来2m行表示m次回忆,每次回忆2行:第1行2个整数u、v,第2行给出回忆的字符串s。

    Output

    对于每次回忆,输出串s出现的次数。

    Sample Input

    12 3
    1 2 w
    2 3 w
    3 4 x
    4 5 w
    5 6 w
    6 7 x
    7 8 w
    8 9 w
    9 10 x
    10 11 w
    11 12 w
    1 7
    wwx
    1 12
    www
    1 12
    w

    Sample Output

    2
    0
    8

    HINT

    对于100%的数据,n<=100000,m<=100000,询问串的总长<=300000

    题解:一开始想反了,以为要把原树建成AC自动机。。。不可做?

    我们将询问分成两段,一段上去的和一段下去的,这样只需要对询问串的正串和反串分别维护AC自动机即可。那么中间的呢?由于总长不超过2K,所以暴力拿出来跑KMP即可。

    考虑每一段,我们采用离线的方法,将询问挂链到树的节点上,在上端的点系数为-1,下端的点系数为+1,然后DFS一遍整棵树,每扫到一个点就将这个点在AC自动机中对应点的权值+1。然后考虑在这个点挂链的所有询问,用询问串对应点在fail树中子树的点权和更新答案即可。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int maxn=100010;
    int n,m,t1,t2,l1,l2,cnt;
    queue<int> Q;
    struct AC
    {
    	int tot;
    	int ch[26],fail;
    }a1[maxn*3],a2[maxn*3];
    struct Fail_Tree
    {
    	int tot;
    	AC *a;
    	vector<int> ch[maxn*3];
    	int p[maxn*3],q[maxn*3],s[maxn*3];
    	void dfs(int x)
    	{
    		p[x]=++p[0];
    		for(int i=0;i<(int)ch[x].size();i++)	dfs(ch[x][i]);
    		q[x]=p[0];
    	}
    	void build()
    	{
    		Q.push(1);
    		int i,u;
    		while(!Q.empty())
    		{
    			u=Q.front(),Q.pop();
    			for(i=0;i<26;i++)
    			{
    				if(u==1)
    				{
    					if(a[u].ch[i])	Q.push(a[u].ch[i]),a[a[u].ch[i]].fail=1;
    					else	a[u].ch[i]=1;
    					continue;
    				}
    				if(!a[u].ch[i])
    				{
    					a[u].ch[i]=a[a[u].fail].ch[i];
    					continue;
    				}
    				Q.push(a[u].ch[i]);
    				a[a[u].ch[i]].fail=a[a[u].fail].ch[i];
    			}
    		}
    		for(i=2;i<=tot;i++)	ch[a[i].fail].push_back(i);
    		dfs(1);
    	}
    	inline void updata(int x,int v)
    	{
    		for(int i=x;i<=tot;i+=i&-i)	s[i]+=v;
    	}
    	inline int query(int x)
    	{
    		int i,ret=0;
    		for(i=x;i;i-=i&-i)	ret+=s[i];
    		return ret;
    	}
    }f1,f2;
    struct QUERY
    {
    	int a,b,u,v,top,ans;
    }q[maxn];
    struct node
    {
    	int org,k;
    	node() {}
    	node(int a,int b) {org=a,k=b;}
    };
    vector<node> b1[maxn],b2[maxn];
    int val[maxn<<1],next[maxn<<1],to[maxn<<1],head[maxn],fa[20][maxn],Log[maxn],nxt[maxn*3],from[maxn],dep[maxn];
    char T[maxn*3],S[maxn];
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    void getfa(int x)
    {
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])
    		fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,from[to[i]]=val[i],getfa(to[i]);
    }
    inline int lca(int a,int b)
    {
    	if(dep[a]<dep[b])	swap(a,b);
    	int i;
    	for(i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	a=fa[i][a];
    	if(a==b)	return a;
    	for(i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	a=fa[i][a],b=fa[i][b];
    	return fa[0][a];
    }
    inline int FA(int x,int y)
    {
    	for(int i=Log[y];i>=0;i--)	if((1<<i)<=y)	y-=(1<<i),x=fa[i][x];
    	return x;
    }
    inline int KMP()
    {
    	int i,j,ret=0;
    	nxt[0]=-1,i=0,j=-1;
    	while(i<l2)
    	{
    		if(j==-1||T[i]==T[j])	nxt[++i]=++j;
    		else	j=nxt[j];
    	}
    	i=j=0;
    	while(i<l1)
    	{
    		if(j==-1||S[i]==T[j])	i++,j++;
    		else	j=nxt[j];
    		if(j==l2)	ret++;
    	}
    	return ret;
    }
    void dfs(int x,int r1,int r2)
    {
    	f1.updata(f1.p[r1],1),f2.updata(f2.p[r2],1);
    	int i,a;
    	for(i=0;i<(int)b1[x].size();i++)
    	{
    		a=b1[x][i].org;
    		q[a].ans+=b1[x][i].k*(f1.query(f1.q[q[a].a])-f1.query(f1.p[q[a].a]-1));
    	}
    	for(i=0;i<(int)b2[x].size();i++)
    	{
    		a=b2[x][i].org;
    		q[a].ans+=b2[x][i].k*(f2.query(f2.q[q[a].b])-f2.query(f2.p[q[a].b]-1));
    	}
    	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[0][x])	dfs(to[i],a1[r1].ch[val[i]],a2[r2].ch[val[i]]);
    	f1.updata(f1.p[r1],-1),f2.updata(f2.p[r2],-1);
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,a,b,u;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),scanf("%s",T),add(a,b,T[0]-'a'),add(b,a,T[0]-'a');
    	dep[1]=1,getfa(1);
    	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]];
    	t1=t2=1;
    	for(i=1;i<=m;i++)
    	{
    		q[i].u=rd(),q[i].v=rd(),q[i].top=lca(q[i].u,q[i].v);
    		scanf("%s",T),l2=strlen(T);
    		for(u=1,j=0;j<l2;j++)
    		{
    			b=T[j]-'a';
    			if(!a1[u].ch[b])	a1[u].ch[b]=++t1;
    			u=a1[u].ch[b];
    		}
    		q[i].a=u;
    		for(u=1,j=l2-1;j>=0;j--)
    		{
    			b=T[j]-'a';
    			if(!a2[u].ch[b])	a2[u].ch[b]=++t2;
    			u=a2[u].ch[b];
    		}
    		q[i].b=u;
    		if(q[i].top!=q[i].u&&q[i].top!=q[i].v)
    		{
    			b=min(l2-1,dep[q[i].u]-dep[q[i].top]),l1=b;
    			for(a=0,j=FA(q[i].u,dep[q[i].u]-dep[q[i].top]-b);j!=q[i].top;a++,j=fa[0][j])	S[a]=from[j]+'a';
    			b=min(l2-1,dep[q[i].v]-dep[q[i].top]),l1+=b;
    			for(a=l1-1,j=FA(q[i].v,dep[q[i].v]-dep[q[i].top]-b);j!=q[i].top;a--,j=fa[0][j])	S[a]=from[j]+'a';
    			q[i].ans+=KMP();
    		}
    		if(dep[q[i].v]-dep[q[i].top]>=l2)
    			b1[q[i].v].push_back(node(i,1)),b1[FA(q[i].v,dep[q[i].v]-dep[q[i].top]-l2+1)].push_back(node(i,-1));
    		if(dep[q[i].u]-dep[q[i].top]>=l2)
    			b2[q[i].u].push_back(node(i,1)),b2[FA(q[i].u,dep[q[i].u]-dep[q[i].top]-l2+1)].push_back(node(i,-1));
    	}
    	f1.a=a1,f2.a=a2,f1.tot=t1,f2.tot=t2;
    	f1.build(),f2.build();
    	dfs(1,1,1);
    	for(i=1;i<=m;i++)	printf("%d
    ",q[i].ans);
    	return 0;
    }//12 3 1 2 w 2 3 w 3 4 x 4 5 w 5 6 w 6 7 x 7 8 w 8 9 w 9 10 x 10 11 w 11 12 w 1 7 wwx 1 12 www 1 12 w
    //8 1 1 2 a 2 3 b 3 4 a 1 5 b 5 6 a 6 7 b 7 8 a 3 7 ab
  • 相关阅读:
    使用C++调用并部署pytorch模型
    相位展开(phase unwrapping)算法研究与实践
    【计算机视觉】图像配准(Image Registration)
    读书笔记 - 《数字图像处理》(更新中...)
    ssh框架复习
    SVN 版本控制
    Spring的jdbcTemplate 与原始jdbc 整合c3p0的DBUtils 及Hibernate 对比 Spring配置文件生成约束的菜单方法
    JDK 动态代理 讨债实例
    Spring 框架配置web.xml 整合web struts
    Spring整合JUnit spring静态对象属性的注入
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7749512.html
Copyright © 2011-2022 走看看