1. 程式人生 > >【BZOJ】5233: [Lydsy2017省隊十連測]壞題-AC自動機&縮點

【BZOJ】5233: [Lydsy2017省隊十連測]壞題-AC自動機&縮點

題解

這題難點在讀題上。。。。 經過多番嘗(shi)試(tan),終於明白了兩端均無限長的鏈的意思。

匹配問題首先建顆AC自動機:將所有為字串終止位置的節點及其failfail鏈上的點都標記起來,記為edi=1ed_i=1,其餘點edi=0ed_i=0

考慮一個兩端均無限長的鏈中不出現這nn個串中任意一個,那麼就是在AC自動機上不經過edi=1ed_i=1的點上繞環

將所有edi=0ed_i=0的點及其ch[i][j](0j<t)ch[i][j](0\leq j<t)edch[i][

j]=0ed_{ch[i][j]}=0的點之間連一條指向chch的有向邊,取出新建的圖,那麼構造出的無限長的串就是這張圖上的一條帶環路徑,每一條不同的路徑代表的串都不同

首先考慮解無窮多的情況:如果一個點在多個環中,那麼這個點出發就會有任意多種組合。所以答案有限的情況的前提就是這張圖中只有簡單環

因為圖上只有簡單環,tarjan縮點以後變成DAG,就可以在DAG上拓撲序DP了。

但現在我們只是滿足了答案有限,對於一個兩端均無限長的鏈還需要一個限制條件:這條路徑的起點和終點都是在一個環內。

那麼問題的本質就是求AC自動機中取出的這張圖上起點終點都為sccscc的不同路徑數

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;
}