【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP

时间:2022-07-26 18:42:12

1194: [HNOI2006]潘多拉的盒子

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 456  Solved: 215
[Submit][Status][Discuss]

Description

【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP 

【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP

Input

第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50)。文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述。每一块的格式如下。 一块的第一行有两个正整数n,m。分别表示该咒语机中元件的个数、咒语源输出元的个数(1≤m≤n≤50)。 接下来一行有m个数,表示m个咒语源输出元的标号(都在0到n-1之间)。接下来有n行,每一行两个数。第i行(0≤i≤n-1)的两个数表示pi,0和pi,1(当然,都在0到n-1之间)。

Output

第一行有一个正整数t,表示最长升级序列的长度。

Sample Input

4
1 1
0
0 0
2 1
0
1 1
0 0
3 1
0
1 1
2 2
0 0
4 1
0
1 1
2 2
3 3
0 0

Sample Output

3

HINT

Source

Solution

这道题还是比较有趣的..如果能够得到包含关系,可以直接拓扑排序dp求解最长链。

然后如何判断是否包含?需要利用自动机的相关性质。

考虑dp,$f[x][y]$表示自动机1和自动机2分别对应状态$x$和$y$,然后枚举转移更新$f[x'][y']$。

考虑什么情况不满足包含,即$f[x][y]=1$且$end[x]=1$且$end[y]=0$表示不包含,否则一定包含。

所以只要暴力的去枚举两两自动机跑即可,主要的思想就是去尝试构造01串使得一个能匹配另一个不能。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
	while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}
int T,N,M;
struct DFANode{
	int son[55][2],end[55];
}DFA[55],A,B;
bool f[55][55],flag;

inline void Run(int x,int y)
{	
	if (!x || !y || f[x][y] || !flag) return;
	f[x][y]=1;
	if (!A.end[x] && B.end[y]) flag=0;
	for (int i=0; i<=1; i++)
		Run(A.son[x][i],B.son[y][i]);
}
inline bool Check(DFANode x,DFANode y)
{
	memset(f,0,sizeof(f));
	A=x,B=y; flag=1;
	Run(1,1);
	return flag;
}

struct GraphNode{
	struct EdgeNode{
		int next,to;
	}edge[55*55];
	int head[55],cnt;
	inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
	inline void InsertEdge(int u,int v) {AddEdge(u,v);}
}G1,G2;

int visit[55],stack[55],top,dfn[55],low[55],dfsn;
int belong[55],size[55],scc;
inline void Tarjan(int x)
{
	visit[x]=1; stack[++top]=x;
	dfn[x]=low[x]=++dfsn;
	for (int i=G1.head[x]; i; i=G1.edge[i].next) 
		if (!dfn[G1.edge[i].to])
			Tarjan(G1.edge[i].to),low[x]=min(low[x],low[G1.edge[i].to]);
		else
			if (visit[G1.edge[i].to]) low[x]=min(dfn[G1.edge[i].to],low[x]);
	if (dfn[x]==low[x]) {
		int tops=0;
		scc++;
		while (tops!=x) {
			tops=stack[top--];
			belong[tops]=scc,visit[tops]=0,size[scc]++;
		}
	}
}
inline void Tarjan() {for (int i=1; i<=T; i++) if (!dfn[i]) Tarjan(i);}

int mp[55][55],d[55];
inline void Rebuild()
{
	for (int x=1; x<=T; x++)
		for (int i=G1.head[x]; i; i=G1.edge[i].next)
			if (belong[x]!=belong[G1.edge[i].to])
				G2.InsertEdge(belong[x],belong[G1.edge[i].to]),d[belong[G1.edge[i].to]]++;
}

queue<int>q;
int dp[55];
inline void Toposort()
{
	for (int i=1; i<=scc; i++) if (!d[i]) q.push(i);
	for (int i=1; i<=scc; i++) dp[i]=size[i];
	while (!q.empty()) {
		int now=q.front(); q.pop();
		for (int i=G2.head[now]; i; i=G2.edge[i].next) {
			dp[G2.edge[i].to]=max(dp[G2.edge[i].to],dp[now]+size[G2.edge[i].to]);
			if (!--d[G2.edge[i].to]) q.push(G2.edge[i].to);
		}
	}
}

int main()
{
	T=read();
	for (int i=1; i<=T; i++) {
		N=read(),M=read();
		for (int x,j=1; j<=M; j++)
			x=read()+1,DFA[i].end[x]=1;
		for (int j=1; j<=N; j++)
			DFA[i].son[j][0]=read()+1,DFA[i].son[j][1]=read()+1;
	}
	for (int i=1; i<=T; i++)
		for (int j=1; j<=T; j++)
			if (i!=j && Check(DFA[i],DFA[j])) 
				G1.InsertEdge(i,j);
	Tarjan();
	Rebuild();
	Toposort();
	int ans=0;
	for (int i=1; i<=scc; i++) ans=max(ans,dp[i]);
	printf("%d\n",ans);
	return 0;
}