【BZOJ1194】[HNOI2006]潘多拉的盒子【BFS】【SCC】【拓扑排序】【DAG最长路】【自动机】

【题目链接】

这题有毒orz。

判断两个自动机是否有升级关系,BFS一次就行了orz。两个自动机都从0开始,同时走0,同时走1。如果一个自动机到达输出点,而另一个没到达,那么没有升级关系。

然后根据升级关系建图,Tarjan缩点,然后跑DAG上最长路就行了。


注意一个强联通内都是互相有升级关系的,跑最长路时要取size而不是取1,WA了一发...

/* Pigonometry */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 55, maxm = maxn * maxn, maxq = 10000, maxs = 10000;

int n, head[maxn], cnt, vis[maxn][maxn], clo;
int dfn[maxn], low[maxn], belong[maxn], size[maxn], tot, sta[maxs], top;
int dp[maxn], mp[maxn][maxn], du[maxn], q[maxq];
bool ins[maxn];

struct _edge {
	int v, next;
} g[maxm << 1];

struct _data {
	int x, y;
};

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

struct automaton {
	int n, m, trans[maxn][2];
	bool out[maxn];

	void read() {
		n = iread(); m = iread();
		for(int i = 1; i <= m; i++) out[iread()] = 1;
		for(int i = 0; i < n; i++) trans[i][0] = iread(), trans[i][1] = iread();
	}
} A[maxn];

inline void add(int u, int v) {
	g[cnt] = (_edge){v, head[u]};
	head[u] = cnt++;
}

inline bool bfs(int s, int d) {
	static _data q[maxq];
	clo++;
	int h = 0, t = 0; q[t++] = (_data){0, 0};
	vis[0][0] = clo;
	while(h != t) {
		_data u = q[h++];
		int x = u.x, y = u.y;
		if(A[s].out[x] && !A[d].out[y]) return 0;
		for(int i = 0; i < 2; i++) {
			int xx = A[s].trans[x][i], yy = A[d].trans[y][i];
			if(vis[xx][yy] != clo) {
				vis[xx][yy] = clo;
				q[t++] = (_data){xx, yy};
			}
		}
	}
	return 1;
}

inline void tarjan(int x) {
	dfn[x] = low[x] = ++clo;
	ins[sta[++top] = x] = 1;
	for(int i = head[x]; ~i; i = g[i].next) {
		int v = g[i].v;
		if(!dfn[v]) tarjan(v), low[x] = min(low[x], low[v]);
		else if(ins[v]) low[x] = min(low[x], dfn[v]);
	}
	if(dfn[x] == low[x]) {
		tot++;
		while(1) {
			int u = sta[top--];
			belong[u] = tot;
			size[tot]++;
			ins[u] = 0;
			if(u == x) break;
		}
	}
}

inline int topo() {
	for(int i = 1; i <= tot; i++) for(int j = 1; j <= tot; j++) if(mp[i][j])
		du[j]++;
	int h = 0, t = 0, res = 1;
	for(int i = 1; i <= tot; i++) if(!du[i]) dp[q[t++] = i] = size[i];
	while(h != t) {
		int u = q[h++];
		for(int i = 1; i <= tot; i++) if(mp[u][i] && dp[i] < dp[u] + size[i]) {
			dp[i] = dp[u] + size[i];
			q[t++] = i;
			res = max(res, dp[i]);
		}
	}
	return res;
}

int main() {
	n = iread();
	for(int i = 1; i <= n; i++) head[i] = -1, A[i].read(); cnt = clo = 0;

	for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(i ^ j && bfs(i, j))
		add(i, j);

	clo = top = 0;
	for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);

	for(int u = 1; u <= n; u++) for(int i = head[u]; ~i; i = g[i].next) if(belong[u] != belong[g[i].v])
		mp[belong[u]][belong[g[i].v]] = 1;

	printf("%d\n", topo());
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值