【BZOJ】5233: [Lydsy2017省队十连测]坏题-AC自动机&缩点

传送门: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 &lt; t ) ch[i][j](0\leq j&lt;t) ch[i][j](0j<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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值