bzoj1924 所驼门王的宝藏

所驼门王的宝藏

题目背景:

bzoj1924

分析:缩点 + 拓扑图上DP最长路

 

这个题,一看就知道是连边然后tarjan强连通缩点,然后直接DAGDP最长路就可以了,重要的问题是如何建边,显然直接全部暴力建边时可能达到n2级别的边数的,然后我们来考虑优化,显然,对于同一行的1种类门,我们可以直接用其中的一个向所有的其他1号门连边,然后其他的分别向这一个连边,这样所有的1号就在一个强连通当中了,而对于这一行的其他门也只需要用其中的一个1号门向它们连边就可以了,因为所有1号门在同一强连通,所以其中之一能够到达,说明全部都能到达,对于同一列的2号门也可以类似处理,最后是三号门,三号门最多只有8条边,所以直接暴力连就可以了,至于如何找到周围是否存在门,可以暴力用map存储所有的门,也可以通过排序来找对应位置,代码写的是排序,然后最后来一发tarjan缩点 + DAG求最长路就可以了

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>

const int MAXN = 1000000 + 10;

int n, r, c;
struct node {
	int x, y, type, id;
} p[MAXN];
int exist[MAXN][3];
std::vector<int> edge[MAXN];

inline void add_edge(int x, int y) {
	edge[x].push_back(y);
}

inline bool comp1(const node &a, const node &b) {
	return (a.x == b.x) ? (a.type < b.type) : (a.x < b.x);
}

inline bool comp2(const node &a, const node &b) {
	return (a.y == b.y) ? (b.type != 2 && (a.type == 2 || a.type < b.type))
		 : (a.y < b.y);
}

inline void read_in() {
	scanf("%d%d%d", &n, &r, &c);
	for (int i = 1; i <= n; ++i) 
		scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].type), p[i].id = i;		
}

inline void build_graph() {
	int pos = 0;
	std::sort(p + 1, p + n + 1, comp1);
 	for (int i = 1; i <= n; ++i)
		if (p[i].x != p[i - 1].x) pos = (p[i].type == 1) ? i : 0;
		else if (pos == 0) continue ;
		else {
			add_edge(p[pos].id, p[i].id);
			if (p[i].type == 1) add_edge(p[i].id, p[pos].id);
		}
	
	for (int i = p[1].x - 1, h1 = 1, h2 = 1, h3 = 1, last = 0, cur = 2; 
		i <= p[n].x; ++i) {
		last = (last + 1) % 3, cur = (cur + 1) % 3;
		for (; p[h1].x <= i + 1 && h1 <= n; ++h1)
			if (p[h1].x == i + 1) exist[p[h1].y][cur] = p[h1].id;
		for (; p[h2].x <= i && h2 <= n; ++h2) 
			if (p[h2].type == 3 && p[h2].x == i)
				for (int j = p[h2].y - 1; j <= p[h2].y + 1; ++j)
					for (int k = 0; k < 3; ++k)
						if (exist[j][k] != 0 && exist[j][k] != p[h2].id)
							add_edge(p[h2].id, exist[j][k]);
		for (; p[h3].x <= i - 1 && h3 <= n; ++h3)
			if (p[h3].x == i - 1) exist[p[h3].y][last] = 0;
	}
		
	std::sort(p + 1, p + n + 1, comp2);
	for (int i = 1; i <= n; ++i)
		if (p[i].y != p[i - 1].y) pos = (p[i].type == 2) ? i : 0;
		else if (pos == 0) continue ;
		else {
			add_edge(p[pos].id, p[i].id);
			if (p[i].type == 2) add_edge(p[i].id, p[pos].id);
		}
}

std::vector<int> s;
int top, o, ind;
std::vector<int> new_edge[MAXN];
int low[MAXN], num[MAXN], scc[MAXN], scnt[MAXN], dp[MAXN];
bool vis[MAXN];

inline void tarjan(int cur) {
	low[cur] = num[cur] = ++ind, vis[cur] = true, s.push_back(cur);
	for (int p = 0; p < edge[cur].size(); ++p) {
		int v = edge[cur][p];
		if (num[v] == 0) tarjan(v), low[cur] = std::min(low[v], low[cur]);
		else if (vis[v]) low[cur] = std::min(low[cur], num[v]);
	}
	if (low[cur] == num[cur]) {
		++top;
		do {
			o = s.back(), vis[o] = false, scc[o] = top;
			scnt[top]++, s.pop_back();
		} while (o != cur);
	}
}

inline int dfs(int cur) {
	if (dp[cur] != 0) return dp[cur];
	else dp[cur] = scnt[cur];
	int temp = 0;
	for (int p = 0; p < new_edge[cur].size(); ++p) {
		int v = new_edge[cur][p];
		temp = std::max(dfs(v), temp);
	}
	return dp[cur] += temp;
}

inline void solve() {
	for (int i = 1; i <= n; ++i) if (num[i] == 0) tarjan(i);
//	for (int i = 1; i <= n; ++i) {
//		std::cout << i << '\n';
//		for (int p = 0; p < edge[i].size(); ++p)
//			std::cout << edge[i][p] << " ";
//		std::cout << '\n';
//	}
	for (int i = 1; i <= n; ++i) 
		for (int p = 0; p < edge[i].size(); ++p) {
			int v = edge[i][p];
			if (scc[i] != scc[v]) new_edge[scc[i]].push_back(scc[v]);
		}
	int ans = 0;
	for (int i = 1; i <= top; ++i) ans = std::max(dfs(i), ans);
	std::cout << ans;
}

int main() {
	read_in();
	build_graph();
	solve();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值