P2403 [SDOI2010]所驼门王的宝藏

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2403 [SDOI2010]所驼门王的宝藏相关的知识,希望对你有一定的参考价值。

P2403 [SDOI2010]所驼门王的宝藏

题意:

R * C的地图上有n个宝藏,给你n个宝藏的坐标,每个宝藏的位置上还有一个传送门,传送门有三种类型,1.可以传送到同行的其他宝藏位置,2.可以传送到同列的其他宝藏位置 3.可以传送到该点周围的八个位置
你可以在任意一个宝藏处开始,问最多获得多少宝藏?

题解:

如果我们直接按照题意要求建边,(暴力建边,第1类门和每行其他门建边,第2,3类门同理),会得到一个有向有环图,对于环我们可以用tarjan进行缩点,然后得到DAG,然后就是在DAG直接dp求就行(拓扑排序)。
d p [ v ] = m a x ( d p [ u ] + v a l [ v ] ) dp[v]=max(dp[u]+val[v] ) dp[v]=max(dp[u]+val[v])
但是数据告诉我们不会这么简单,N<=100000,R<=1000000,C<=1000000
边的数量巨大,可达O( n 2 n^2 n2),所以需要一个巧妙的建边方法来降低复杂度
原始的建边是两两之间建立练习,避免不了是O( n 2 n^2 n2),现在我们可以这样想,对于每行,每列都建立一个额外的点,然后让所有点与这个额外的点建立联系。
第i行的额外的点为x,我们让x向改行所有宝藏建一个边,如果有一个点y是1号门,我们就让y再向x建一个边,通过这样操作,y就是本行任意一个宝藏位置(很妙,这个思路很妙)
列的同理
对于第三种门,我们就暴力枚举八个位置,然后在所有宝藏中二分寻找(事先要对宝藏位置排序),看是否能找到,找到即建边
思路捋清楚,就开干码吧

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
   ll s=0,w=1ll;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
clock_t startTime, endTime;
void rd_test(){
	#ifdef ONLINE_JUDGE
	#else
		startTime = clock(); //计时开始
        freopen("sotomon.in","r",stdin);
	#endif
}
void Time_test(){
	#ifdef ONLINE_JUDGE
	#else
		endTime = clock(); //计时结束
   		printf("\\n运行时间为:%lfs\\n",(double)(endTime - startTime) / CLOCKS_PER_SEC);
	#endif
}
const int N = 2e5+9, T = 2100007, M = 1000007;
const int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1};
const int dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};
int n, r, c, t, edc, edu[M], edv[M];
int ecnt, head[T], nxt[M], vet[M];
int qhead, qtail, que[T], f[T], in[T];
int stac[T], top, val[T], col[T], stamp, dfn[T], low[T], cnt;
bool instac[T];
struct Node {
	int x, y, t;
	bool operator <(const Node &ano) const { 
		return x < ano.x || x == ano.x && y < ano.y;
	}
} a[N]; 

inline void add(int u, int v) {
	edu[++edc] = u; edv[edc] = v;
	
	vet[++ecnt] = v; nxt[ecnt] = head[u];
	head[u] = ecnt;
}

inline void eadd(int u, int v) {
	vet[++ecnt] = v; nxt[ecnt] = head[u];
	head[u] = ecnt;
}

void tarjan(int u) {
	dfn[u] = low[u] = ++stamp;
	stac[++top] = u; instac[u] = true;
	for (int e = head[u]; e; e = nxt[e]) {
		int v = vet[e];
		if (!dfn[v]) {
			tarjan(v); low[u] = min(low[u], low[v]);
		} else if (instac[v]) 
			low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) {
		col[u] = ++cnt; val[cnt] = u > r + c;
		while (stac[top] != u) {
			col[stac[top]] = cnt;
			val[cnt] += (stac[top] > r + c);
			instac[stac[top--]] = false;
		}
		instac[stac[top--]] = false;
	}
}

int getid(int x, int y) {
	int l = 1, r = n;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (a[mid].x == x && a[mid].y == y) return mid;
		else if (a[mid].x < x || a[mid].x == x && a[mid].y < y) l = mid + 1;
		else r = mid - 1;
	}
	return -1;
}

int main() {
	rd_test();
	scanf("%d%d%d", &n, &r, &c);
	for (int i = 1; i <= n; ++i) 
		scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].t);
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; ++i) {
		//处理额外点对当前点的连边
		add(a[i].x, r + c + i); 
		add(r + a[i].y, r + c + i);
		//处理当前点对其它点的连边
		if (a[i].t == 1) add(r + c + i, a[i].x);
		else if (a[i].t == 2) add(r + c + i, r + a[i].y);
		else {
			for (int k = 0; k < 8; ++k) {
				int x = a[i].x + dx[k], y = a[i].y + dy[k];
				if (x >= 1 && x <= r && y >= 1 && y <= c) {
					int id = getid(x, y);
					if (id != -1) add(r + c + i, r + c + id);
				} 
			}
		}
	}
	//缩点 
	t = r + c + n; 
	for (int i = 1; i <= t; ++i)
		if (!dfn[i]) 
			tarjan(i);
	ecnt = 0; 
	memset(head, 0, sizeof(head));
	
	for(int i = 1; i <= edc; ++i)
		if (col[edu[i]] != col[edv[i]]) {
		 	eadd(col[edu[i]], col[edv[i]]);
		 	++in[col[edv[i]]];
		}
	//拓扑排序
	qhead = 0; qtail = -1;
	for (int i = 1; i <= cnt; ++i) 
		if (!in[i]) {
			que[++qtail] = i;
			f[i] = val[i];
		}
	while (qhead <= qtail) {
		int u = que[qhead++];
		for (int e = head[u]; e; e = nxt[e]) {
			int v = vet[e];
			f[v] = max(f[v], f[u] + val[v]);
			if (--in[v] == 0) que[++qtail] = v;
		}
	}
	//统计答案
	int ans = 0;
	for (int i = 1; i <= cnt; ++i)
		ans = max(ans, f[i]);
	printf("%d\\n", ans);
	Time_test();
	return 0;
}

以上是关于P2403 [SDOI2010]所驼门王的宝藏的主要内容,如果未能解决你的问题,请参考以下文章

P2403 [SDOI2010]所驼门王的宝藏

P2403 [SDOI2010]所驼门王的宝藏

[SDOI2010] 所驼门王的宝藏

[SDOI2010]所驼门王的宝藏

洛谷2403 [SDOI2010]所驼门王的宝藏

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