1924: [Sdoi2010]所驼门王的宝藏

15 篇文章 0 订阅
9 篇文章 0 订阅

1924: [Sdoi2010]所驼门王的宝藏

Time Limit: 5 Sec   Memory Limit: 128 MB
Submit: 875   Solved: 359
[ Submit][ Status][ Discuss]

Description

Input

第一行给出三个正整数 N, R, C。 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti。Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意一列的“横天门”,2表示可以传送到任意一行第 yi列的“纵寰门”,3表示可以传送到周围 8格宫室的“自由门”。 保证 1≤xi≤R,1≤yi≤C,所有的传送门位置互不相同。

Output

只有一个正整数,表示你确定的路线所经过不同藏宝宫室的最大数目。

Sample Input

10 7 7
2 2 1
2 4 2
1 7 2
2 7 3
4 2 2
4 4 1
6 7 3
7 7 1
7 5 2
5 2 1

Sample Output

9

HINT

测试点编号 N R C 1 16 20 20 2 300 1,000 1,000 3 500 100,000 100,000 4 2,500 5,000 5,000 5 50,000 5,000 5,000 6 50,000 1,000,000 1,000,000 7 80,000 1,000,000 1,000,000 8 100,000 1,000,000 1,000,000 9 100,000 1,000,000 1,000,000 10 100,000 1,000,000 1,000,000

Source

[ Submit][ Status][ Discuss]

如果有些门之间构成环,那么当然是都走一遍最优啦--
如果只在门与门之间建边,边数是O(n^2)的
转移的方式是有限制的,,同行or同列
那么每行虚拟一个点,每列虚拟一个点,首先虚拟点向每个属于自己行/列的点连边
然后每个点如果是走向当前行/列的门,那么这个点向虚拟点连边
第三类门,,边不超过8条,暴力连
在新图上跑个tarjan,所有环就都出来了

点的话,,一开始用hash存,但是开太大MLE两次。。GG
mo取999983,,貌似冲突次数不会很多??
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
using namespace std;

const int maxn = 1E5 + 10;
const int maxm = 1E6 + 10;
const int mo = 999983;
const int dx[8] = {0,1,0,-1,1,-1,1,-1};
const int dy[8] = {1,0,-1,0,1,1,-1,-1};

int n,R,C,cnt,tot,dfs_clock,low[maxm],r[maxn],c[maxn],typ[maxn],Ans[maxm]
	,va[maxm],numr[maxm],numc[maxm],dfn[maxm],belong[maxm],du[maxm],siz[maxm];

vector <int> v1[mo];
vector <int> v2[maxm];
vector <int> v3[maxm];
stack <int> s;
queue <int> Q;

void BFS()
{
	for (int i = 1; i <= cnt; i++)
		for (int j = 0; j < v2[i].size(); j++) {
			int to = v2[i][j];
			if (belong[to] == belong[i]) continue;
			v3[belong[to]].push_back(belong[i]);
			++du[belong[i]];
		}
	for (int i = 1; i <= tot; i++)
		if (!du[i]) Q.push(i);
	while (!Q.empty()) {
		int k = Q.front(); Q.pop();
		Ans[k] = siz[k] + Ans[k];
		for (int i = 0; i < v3[k].size(); i++) {
			int to = v3[k][i];
			Ans[to] = max(Ans[to],Ans[k]);
			--du[to];
			if (!du[to]) Q.push(to);
		}
	}
}

void hash_insert(int x,int y,int now)
{
	int pos = (1LL*(x-1)*y%mo + y)%mo;
	v1[pos].push_back(now);
}

int hash_search(int x,int y)
{
	int pos = (1LL*(x-1)*y%mo + y)%mo;
	for (int i = 0; i < v1[pos].size(); i++) {
		int now = v1[pos][i];
		if (r[now] == x && c[now] == y) return now;
	}
	return 0;
}

void Dfs(int x)
{
	low[x] = dfn[x] = ++dfs_clock;
	s.push(x);
	for (int i = 0; i < v2[x].size(); i++) {
		int to = v2[x][i];
		if (!dfn[to]) Dfs(to),low[x] = min(low[x],low[to]);
		else if (!belong[to]) low[x] = min(low[x],low[to]);
	}
	if (dfn[x] == low[x]) {
		++tot;
		for (;;) {
			int y = s.top(); s.pop();
			belong[y] = tot;
			siz[tot] += va[y];
			if (y == x) break;
		}
	}
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n >> R >> C; cnt = n;
	for (int i = 1; i <= n; i++) {
		scanf("%d%d%d",&r[i],&c[i],&typ[i]);
		hash_insert(r[i],c[i],i);
	}
	for (int i = 1; i <= n; i++) {
		if (typ[i] == 1) {
			if (!numr[r[i]]) numr[r[i]] = ++cnt;
			v2[i].push_back(numr[r[i]]);
		}
		else if (typ[i] == 2) {
			if (!numc[c[i]]) numc[c[i]] = ++cnt;
			v2[i].push_back(numc[c[i]]);
		}
		else {
			for (int l = 0; l < 8; l++) {
				int xx = r[i] + dx[l];
				int yy = c[i] + dy[l];
				if (xx <= 0 || xx > R || yy <= 0 || yy > C) continue;
				int ret = hash_search(xx,yy);
				if (!ret) continue;
				v2[i].push_back(ret);
			}
		}
		va[i] = 1;
	} 
	for (int i = 1; i <= n; i++) {
		if (numr[r[i]]) v2[numr[r[i]]].push_back(i);
		if (numc[c[i]]) v2[numc[c[i]]].push_back(i);
	}
	for (int i = 1; i <= cnt; i++) if (!dfn[i]) Dfs(i);
	BFS(); int ans = 0;
	for (int i = 1; i <= tot; i++) ans = max(ans,Ans[i]);
	cout << ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值