Link
我们考虑两类路径:
\(0\)类:字符路径只比点路径少了最后一个字符。
\(1\)类:字符路径与点路径相等。
\(2\)类:字符路径只比点路径少了第一个字符。
设\(f_{flg,u,l}\)表示到\(u\)点结束,长度为\(l\)的\(flg\)类路径条数。
那么长度为\(l\)的合法路径条数就是\(\sum\limits_{u=1}^nf_{1,u,l}\)。
初始状态为\(f_{0,u,0}=1\)。
转移就是加一条路径接在当前路径的后面,按\(flg\)状态分成四类讨论:
\(0\rightarrow0\)
接在后面的一定是一条\(0\)类路径,而它的最后一条边\((u,v)\)上的字符串一定以\(u\)结尾。
那么我们枚举这样的边,从该边暴力往前拓展,得到一条可能合法的\(0\)类路径\((p,v)\)。
如果该路径合法,那么它会造成转移\(f_{0,p,l}\rightarrow f_{0,v,l+len(p,v)}\)。
\(1\rightarrow1\)
接在后面的一定是一条\(2\)类路径,而它的第一条边\((u,v)\)上的字符串一定以\(v\)开头。
那么我们枚举这样的边,从该边暴力往前拓展,得到一条可能合法的\(2\)类路径\((p,v)\)。
如果该路径合法,那么它会造成转移\(f_{1,p,l}\rightarrow f_{1,v,l+len(p,v)}\)。
\(1\rightarrow0\)
对于任意一条字符串为空的边\((u,v)\),它会造成转移\(f_{1,u,l}\rightarrow f_{1,v,l+1}\)。
\(0\rightarrow1\)
接在后面的一定是一条\(1\)类路径,这条路径上一定存在一条边\((u,v)\)满足\(uv\)是该边上的字符串的子串。
那么我们枚举这样的边,从该边暴力往两头拓展,得到一条可能的\(1\)类路径\((p,q)\)。
如果该路径合法,那么它会造成转移\(f_{0,p,l}\rightarrow f_{1,q,l+len(p,q)}\)。
不难发现转移可以写成\((o,o',u,v,\Delta)\)的形式,对每条上述提到的类型的边预处理这样的五元组,然后再根据拓扑序(按\(l\)升序)对\(f\)转移即可。
时间复杂度为\(O(n^3)\)。
E1找路径的做法包含在\(0\rightarrow1\)转移部分了。
#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\n",sum);
}
}