传送门:bzoj5233
题解
这题难点在读题上。。。。
经过多番尝(shi)试(tan),终于明白了两端均无限长的链的意思。
匹配问题首先建颗AC自动机:将所有为字符串终止位置的节点及其 f a i l fail fail链上的点都标记起来,记为 e d i = 1 ed_i=1 edi=1,其余点 e d i = 0 ed_i=0 edi=0。
考虑一个两端均无限长的链中不出现这 n n n个串中任意一个,那么就是在AC自动机上不经过 e d i = 1 ed_i=1 edi=1的点上绕环。
将所有 e d i = 0 ed_i=0 edi=0的点及其 c h [ i ] [ j ] ( 0 ≤ j < t ) ch[i][j](0\leq j<t) ch[i][j](0≤j<t)中 e d c h [ i ] [ j ] = 0 ed_{ch[i][j]}=0 edch[i][j]=0的点之间连一条指向 c h ch ch的有向边,取出新建的图,那么构造出的无限长的串就是这张图上的一条带环路径,每一条不同的路径代表的串都不同。
首先考虑解无穷多的情况:如果一个点在多个环中,那么这个点出发就会有任意多种组合。所以答案有限的情况的前提就是这张图中只有简单环。
因为图上只有简单环,tarjan缩点以后变成DAG,就可以在DAG上拓扑序DP了。
但现在我们只是满足了答案有限,对于一个两端均无限长的链还需要一个限制条件:这条路径的起点和终点都是在一个环内。
那么问题的本质就是求AC自动机中取出的这张图上起点终点都为 s c c scc scc的不同路径数。
复杂度 O ( 10 n t ) O(10nt) O(10nt)
p.s.
考虑到一种特殊的情况:一条路径途中也有环,答案似乎就是无限的了。但这种情况根本不存在,它已经在一个点在两个环中的情况中算到了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<queue>
#include<cstdlib>
#include<vector>
#include<map>
using namespace std;
const int N=1e4+10;
int fir,n,ans,cnt,bel[N],inq[N];
int head[N],to[N*6],nxt[N*6],tot;
int df[N],low[N],stk[N],dfn,top;
int ind[N],dp[N],cir,jud,selc[N],isc[N];
char s[15];
vector<int>hv[N],g[N];
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;ind[v]++;}
struct ACT{
queue<int>que;
int len,ch[N][7],f[N],ed[N];
inline void ins()
{
register int i,j,u=0,alp;
scanf("%s",s+1);
len=strlen(s+1);
for(i=1;i<=len;++i){
alp=s[i]-'a';
if(!ch[u][alp]) ch[u][alp]=++cnt;
u=ch[u][alp];
}
ed[u]=1;
}
inline void build()
{
register int i,j,x,y,z,u;
for(i=0;i<fir;++i) if(ch[0][i])
que.push(ch[0][i]);
for(;!que.empty();){
x=que.front();que.pop();y=f[x];
for(i=0;i<fir;++i){
z=ch[x][i];u=ch[y][i];
if(!z) {ch[x][i]=u;continue;}
f[z]=u;ed[z]|=ed[u];que.push(z);
}
}
}
inline void lik()
{
int i,j,cot=0;
for(i=0;i<=cnt;++i) if(!ed[i]){
for(j=0;j<fir;++j)
if(!ed[ch[i][j]]){
g[i].push_back(ch[i][j]);
if(i==ch[i][j]) selc[i]++;
}
}
}
}ac;
void dfs(int x)
{
df[x]=low[x]=++dfn;stk[++top]=x;inq[x]=1;
int i,j;
for(i=g[x].size()-1;~i;--i){
j=g[x][i];
if(!df[j]){
dfs(j);low[x]=min(low[x],low[j]);
}else if(inq[j]) low[x]=min(low[x],df[j]);
}
if(low[x]==df[x]){
++cir;
for(;stk[top+1]!=x;--top){
i=stk[top];
inq[i]=0;bel[i]=cir;
hv[cir].push_back(i);
}
if(hv[cir].size()==1 && (!selc[x])){
isc[cir]=0;
}else isc[cir]=1;
}
}
void ck(int u)
{
if(jud) return;
inq[u]=1;
for(int j,i=g[u].size()-1;~i;--i){
j=g[u][i];
if(inq[j]){inq[j]++;if(inq[j]>2) {jud=1;return;}}
else{ck(j);if(jud) return;}
}
inq[u]=0;
}
queue<int>Q;
inline void mkk()
{
int i,j,k,t,x,y;
for(i=1;i<=cir;++i)
{
for(j=hv[i].size()-1;~j;--j){
k=hv[i][j];
for(x=g[k].size()-1;~x;--x){
y=g[k][x];
if(i!=bel[y])
lk(i,bel[y]);
}
}
}
for(i=1;i<=cir;++i) if(!ind[i]) Q.push(i);
for(;!Q.empty();){
x=Q.front();Q.pop();
dp[x]+=isc[x];
if(isc[x]) ans+=dp[x];
for(i=head[x];i;i=nxt[i]){
j=to[i];ind[j]--;
dp[j]+=dp[x];
if(!ind[j]) Q.push(j);
}
}
printf("%d\n",ans);
}
int main(){
scanf("%d%d",&fir,&n);
for(register int i=1;i<=n;++i) ac.ins();
ac.build();ac.lik();
jud=0;ck(0);
if(jud) {puts("-1");return 0;}
for(int i=0;i<=cnt;++i) if(!df[i]) dfs(i);
mkk();
return 0;
}