CF331E2 Deja Vu

Link
我们考虑两类路径:
(0)类:字符路径只比点路径少了最后一个字符。
(1)类:字符路径与点路径相等。
(2)类:字符路径只比点路径少了第一个字符。
(f_{flg,u,l})表示到(u)点结束,长度为(l)(flg)类路径条数。
那么长度为(l)的合法路径条数就是(sumlimits_{u=1}^nf_{1,u,l})
初始状态为(f_{0,u,0}=1)
转移就是加一条路径接在当前路径的后面,按(flg)状态分成四类讨论:

(0 ightarrow0)

接在后面的一定是一条(0)类路径,而它的最后一条边((u,v))上的字符串一定以(u)结尾。
那么我们枚举这样的边,从该边暴力往前拓展,得到一条可能合法的(0)类路径((p,v))
如果该路径合法,那么它会造成转移(f_{0,p,l} ightarrow f_{0,v,l+len(p,v)})

(1 ightarrow1)

接在后面的一定是一条(2)类路径,而它的第一条边((u,v))上的字符串一定以(v)开头。
那么我们枚举这样的边,从该边暴力往前拓展,得到一条可能合法的(2)类路径((p,v))
如果该路径合法,那么它会造成转移(f_{1,p,l} ightarrow f_{1,v,l+len(p,v)})

(1 ightarrow0)

对于任意一条字符串为空的边((u,v)),它会造成转移(f_{1,u,l} ightarrow f_{1,v,l+1})

(0 ightarrow1)

接在后面的一定是一条(1)类路径,这条路径上一定存在一条边((u,v))满足(uv)是该边上的字符串的子串。
那么我们枚举这样的边,从该边暴力往两头拓展,得到一条可能的(1)类路径((p,q))
如果该路径合法,那么它会造成转移(f_{0,p,l} ightarrow f_{1,q,l+len(p,q)})

不难发现转移可以写成((o,o',u,v,Delta))的形式,对每条上述提到的类型的边预处理这样的五元组,然后再根据拓扑序(按(l)升序)对(f)转移即可。
时间复杂度为(O(n^3))
E1找路径的做法包含在(0 ightarrow1)转移部分了。

#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using std::deque;
const int N=57,P=1000000007;
int n,m,f[2][N][2*N],e[N][N];std::vector<int>str[N][N],trans[2][2][N][N];
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int extend(deque<int>&q,deque<int>::iterator it,int o)
{
    int flg=1;
    if(!~o)
	for(auto pre=prev(it);it!=q.begin()&&(int)q.size()<=n+n;it=pre,pre=prev(it))
	    flg&=e[*pre][*it],q.insert(q.begin(),str[*pre][*it].begin(),str[*pre][*it].end());
    else
	for(auto nex=next(it);nex!=q.end()&&(int)q.size()<=n+n;it=nex,nex=next(it))
	    flg&=e[*it][*nex],q.insert(q.end(),str[*it][*nex].begin(),str[*it][*nex].end());
    return flg&((int)q.size()<=n+n);
}
void init()
{
    for(int i=1;i<=n;++i)
	for(int j=1;j<=n;++j)
	    if(str[i][j].size()&&str[i][j].back()==i)
	    {
		deque<int>q(str[i][j].begin(),str[i][j].end());
		if(extend(q,prev(q.end()),-1)) trans[0][0][q.front()][j].push_back((int)q.size());
	    }
    for(int i=1;i<=n;++i)
	for(int j=1;j<=n;++j)
	{
	    auto p=std::find(str[i][j].begin(),str[i][j].end(),i);
	    if(p==str[i][j].end()||++p==str[i][j].end()||*p!=j) continue;
	    deque<int>q(str[i][j].begin(),str[i][j].end());
	    auto it=q.begin()+(p-str[i][j].begin())-1;
	    if(extend(q,it,-1)&&extend(q,it+1,1)) trans[0][1][q.front()][q.back()].push_back((int)q.size()-1);
	}
    for(int i=1;i<=n;++i)
	for(int j=1;j<=n;++j)
	    if(e[i][j]&&str[i][j].empty())
		trans[1][0][i][j].push_back(1);
    for(int i=1;i<=n;++i)
	for(int j=1;j<=n;++j)
	    if(str[i][j].size()&&str[i][j].front()==j)
	    {
		deque<int>q(str[i][j].begin(),str[i][j].end());
		if(extend(q,q.begin(),1)) trans[1][1][i][q.back()].push_back((int)q.size());
	    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i) f[0][i][0]=1;
    for(int i=1,u,v,k;i<=m;++i) for(u=read(),v=read(),k=read(),e[u][v]=1;k;--k) str[u][v].push_back(read());
    init();
    for(int l=0;l<n+n;++l)
	for(int i=0;i<2;++i)
	    for(int j=1;j<=n;++j)
		if(f[i][j][l])
		    for(int p=0;p<2;++p)
			for(int q=1;q<=n;++q)
			    for(int x:trans[i][p][j][q])
				if(x+l<=n+n)
				    inc(f[p][q][x+l],f[i][j][l]);
    for(int i=1;i<=n+n;++i)
    {
	int sum=0;
	for(int j=1;j<=n;++j) inc(sum,f[1][j][i]);
	printf("%d
",sum);
    }
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12918017.html