NOIP模拟(10.31)T2 朋友 (bzoj2143 飞飞侠)

30 篇文章 0 订阅
15 篇文章 0 订阅

朋友

题目背景:

10.31 NOIP模拟T2 bzoj2143

分析:线段树优化建图 or 最短路 + 等效建图

 

考场上一眼n棵线段树优化建图,然后尝试感觉了一下,发现如果给5秒的话,复杂度好像很稳啊,然而当时忽略了空间只有128M这个严肃的事实(事实证明,空间128M标算都搞不过去······

讲题,先来讲我的做法,考虑对于每一个点而言,它在每一行中可以跳到的点应该是一段连续的区间,那么如果我们对于每一行建一颗线段树,那么每一行最多对应了log n个结点,也就是说,对于每一个点来说,有nlogn条边,总的边数就应该是n3logn因为数据才200logn相当于就是常数了,然后总的点数在n2级别,跑dijkstra的复杂度就是(n3logn) * log(n3logn)理论上应该是可以过去的,实践也证明是可以的最慢的点大概也就只有2.5s左右,然而·······本来这个题有一个G的空间,教练卡成了128M,我也是······所以就GG50啦······然而bzoj上跑的飞快······具体实现也比较简单,对于每一个点向n棵线段树对应的可行区间连边,然后在线段树中从上层向下层连边权为0的边,表示如果可以到总的区间,则一定可以到对应的子区间,然后对于叶子节点,向其对应的起始点连边,边权为0,表示,如果可以到达这个点,则一定可以从这个点出发。然后跑3次最短路即可。(有同学将左边的出发点也建了树,事实证明没有那个必要啊······)

Source:

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

const int MAXN = 200 + 10;
const long long INF = 1000000000000000000;

int n, m, ind, pos, times;
int id[MAXN][MAXN], to[MAXN][MAXN << 2];
int a[MAXN][MAXN], b[MAXN][MAXN], x[4], y[4];

struct node {
	int to;
	long long w;
	node(int to = 0, long long w = 0) : to(to), w(w) {}
	inline bool operator < (const node &a) const {
		return w > a.w;
	}
} ;

std::vector<node> edge[MAXN * MAXN * 3];

inline void add_edge(int x, int y, long long w) {
	++times;
	edge[x].push_back(node(y, w));
}

inline void build(int k, int l, int r, int *tree) {
	tree[k] = ++ind;
	if (l == r) return add_edge(tree[k], id[pos][l], 0);
	int mid = l + r >> 1;
	build(k << 1, l, mid, tree), build(k << 1 | 1, mid + 1, r, tree);
	add_edge(tree[k], tree[k << 1], 0), add_edge(tree[k], tree[k << 1 | 1], 0);
}

inline void add_edge(int cur, int w, int k, int l, int r, int ql, int qr, 
	int *tree) {
	if (ql <= l && r <= qr) return add_edge(cur, tree[k], w);
	int mid = l + r >> 1;
	if (ql <= mid) add_edge(cur, w, k << 1, l, mid, ql, qr, tree);
	if (qr > mid) add_edge(cur, w, k << 1 | 1, mid + 1, r, ql, qr, tree);
}

inline int get_id(int k, int l, int r, int pos, int *tree) {
	if (l == r) return tree[k];
	int mid = l + r >> 1;
	if (pos <= mid) return get_id(k << 1, l, mid, pos, tree);
	else return get_id(k << 1 | 1, mid + 1, r, pos, tree);
}

inline void read_in() {
	scanf("%d%d", &n, &m), ind = n * m;
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			scanf("%d", &a[i][j]);
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			scanf("%d", &b[i][j]);
}

inline void build_graph() {
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			id[i][j] = (i - 1) * m + j;
	for (int i = 1; i <= n; ++i) pos = i, build(1, 1, m, to[i]);
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) {
			int up = std::max(i - a[i][j], 1), down = std::min(i + a[i][j], n);
			for (int k = up; k <= down; ++k) {
				int l = std::max(j - (a[i][j] - abs(i - k)), 1);
				int r = std::min(j + (a[i][j] - abs(i - k)), m);
				add_edge(id[i][j], b[i][j], 1, 1, m, l, r, to[k]);
			}	
		}
	for (int i = 1; i <= 3; ++i) scanf("%d%d", &x[i], &y[i]);
}

long long d[3];
inline void dijkstra(int s, int t1, int t2) {
	static long long dis[MAXN * MAXN * 4];
	static bool vis[MAXN * MAXN * 4], f1, f2;
	for (int i = 1; i <= ind; ++i) dis[i] = INF, vis[i] = false;
	std::priority_queue<node> q;
	dis[s] = 0, q.push(node(s, dis[s])), f1 = f2 = false;
	while (!q.empty()) {
		while (!q.empty() && vis[q.top().to]) q.pop();
		if (q.empty()) break ;
		int cur = q.top().to;
		(cur == t1) ? (f1 = true) : 0, (cur == t2) ? (f2 = true) : 0;
		if (f1 && f2) break ;
		vis[cur] = true;
		for (int p = 0; p < edge[cur].size(); ++p) {
			node *e = &edge[cur][p];
			if (!vis[e->to] && dis[e->to] > dis[cur] + e->w)
				dis[e->to] = dis[cur] + e->w, q.push(node(e->to, dis[e->to]));
		}
	}
	d[1] = dis[t1], d[2] = dis[t2];
}

inline void solve() {
	long long ans = INF * 5, dis[4][4];
	char c;
	int id1 = get_id(1, 1, m, y[1], to[x[1]]);
	int id2 = get_id(1, 1, m, y[2], to[x[2]]);
	int id3 = get_id(1, 1, m, y[3], to[x[3]]);
	dijkstra(id[x[1]][y[1]], id2, id3), dis[1][2] = d[1], dis[1][3] = d[2];
	dijkstra(id[x[2]][y[2]], id1, id3), dis[2][1] = d[1], dis[2][3] = d[2];
	dijkstra(id[x[3]][y[3]], id1, id2), dis[3][1] = d[1], dis[3][2] = d[2];
	if (x[1] == x[2] && y[1] == y[2]) dis[1][2] = dis[2][1] = 0;
	if (x[1] == x[3] && y[1] == y[3]) dis[1][3] = dis[3][1] = 0;
	if (x[2] == x[3] && y[2] == y[3]) dis[2][3] = dis[3][2] = 0;
	(dis[2][1] + dis[3][1] < ans) ? (c = 'X', ans = dis[2][1] + dis[3][1]) : 0;
	(dis[1][2] + dis[3][2] < ans) ? (c = 'Y', ans = dis[1][2] + dis[3][2]) : 0;
	(dis[1][3] + dis[2][3] < ans) ? (c = 'Z', ans = dis[1][3] + dis[2][3]) : 0;
	std::cout << c << '\n' << ans;
}

int main() {
//	freopen("friend.in", "r", stdin);
//	freopen("friend.out", "w", stdout);
	read_in();
	build_graph();
	solve();
	return 0;
}

下面是标算做法,我们在这里引入一个层级的定义,对于每一个点,我们将其拆成S个点,S为最大的弹射距离(显然S <= n + m - 2),然后对于一个点point(x, y, h)表示,当前点的坐标为(x, y),是级别h的点,我们将它向相邻的级别比它小一的点以及自己的下一级别,连边权为0的边,即:

point(x, y, h) à point(x, y, h - 1)

point(x, y, h) à point(x - 1, y, h - 1)

point(x, y, h) à point(x, y - 1, h - 1)

point(x, y, h) à point(x + 1, y, h - 1)

point(x, y, h) à point(x, y + 1, h - 1)

我们可以将这个级别h理解为它还能完全不需要花费的走多少步,对于一个点point(x, y, h)它就可以0花费走到左右和它距离不超过h的点,然后对于每一个地面节点,即h0的节点point(x, y, 0),我们可以利用弹射机将它放到一个高度为a[x][y]point(x, y, a[i][j])上,即

point(x, y, 0) à point(x, y, a[i][j])边权为b[i][j],表示通过话费b[i][j],使得你可以从当前节点向周围走最多a[i][j]步,这样就大大减少了原题边数到n3级别然后这样就将问题转化成了,求三个地面节点两两到达的最短路径,直接3dijkstra即可,注意到每个点最多只有5条边,点的数量和边的数量是同阶的,都是n3,所以dijkstra的复杂度就是n3log(n3)······················

然而上面这个复杂度是在扯淡,我们来考虑一下,若n = m,那么总点数为n3 * 2,总边数为n3 * 10,然后我们回去看看我之前的线段树,是不是n3log n啊,但是,考虑线段树的log n不是每一次都有满满的log n个区间的(也不可能有),并且,除非所有的a[i][j]都能覆盖整个图,否则有一个n也是不满的,再者,退一万步讲,这道题的n是不是200啊,你确定log 200 > 10 !!!!所以说这个标算复杂度完全相当于加了一个log,还不止啊,完全没有线段树优秀啊,事实证明时间上也比线段树多了两倍左右啊······一点都不优秀······(虽然思路的确妙妙)而且你算算空间复杂度,200 * 200 * 400个点,你开一个dis数组,要用long long你就已经快256M了吧·······

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 = 200 + 10;
const int MAXM = 16000000 + 10;
const long long INF = 1000000000000000000;

int n, m, ind;
int a[MAXN][MAXN];
long long b[MAXN][MAXN];
int id[MAXN][MAXN][MAXN << 1 | 1];
int x[4], y[4], max;

struct point {
	int x, y, h;
	point(int x = 0, int y = 0, int h = 0) : x(x), y(y), h(h) {}
	inline bool operator == (const point &a) const {
		return (x == a.x) && (y == a.y) && (h == a.h);
	}
} ;

struct node {
	point to;	
	long long w;
	node() {}
	node(point to, long long w) : to(to), w(w) {}
	inline bool operator < (const node &a) const {
		return w > a.w;
	}
} ;

inline void read_in() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			scanf("%d", &a[i][j]), max = std::max(max, a[i][j]);
	for (int i = 1; i <= n; ++i) 
		for (int j = 1; j <= m; ++j) 
			scanf("%d", &b[i][j]);
	for (int i = 1; i <= 3; ++i) scanf("%d%d", &x[i], &y[i]);
	max = std::min(max, n + m - 2);
}

inline void build_graph() {
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			for (int s = 0; s <= max; ++s)
				id[i][j][s] = ++ind;
}

inline int get_id(point x) {
	return id[x.x][x.y][x.h];
}

long long dis[MAXM];
inline void dijkstra(point s, point t1, point t2) {
	static bool vis[MAXM], f1, f2;
	for (int i = 1; i <= ind; ++i) dis[i] = INF, vis[i] = false;
	std::priority_queue<node> q;
	dis[get_id(s)] = 0, q.push(node(s, dis[get_id(s)])), f1 = f2 = false;
	while (!q.empty()) {
		while (!q.empty() && vis[get_id(q.top().to)]) q.pop();
		if (q.empty()) break ;
		point cur = q.top().to;
		(cur == t1 ? f1 = true : 0), (cur == t2 ? f2 = true : 0);
		if (f1 && f2) break ;
		int x = cur.x, y = cur.y, h = cur.h, c_id = id[x][y][h], v;
		vis[c_id] = true, q.pop();
		if (h != 0) {
			if (dis[v = id[x][y][h - 1]] > dis[c_id]) 
				dis[v] = dis[c_id], q.push(node(point(x, y, h - 1), dis[v]));
			if (x != 1 && dis[v = id[x - 1][y][h - 1]] > dis[c_id]) 
				dis[v] = dis[c_id], q.push(node(point(x - 1, y, h - 1), dis[v]));
			if (y != 1 && dis[v = id[x][y - 1][h - 1]] > dis[c_id])
				dis[v] = dis[c_id], q.push(node(point(x, y - 1, h - 1), dis[v]));
			if (x != n && dis[v = id[x + 1][y][h - 1]] > dis[c_id]) 
				dis[v] = dis[c_id], q.push(node(point(x + 1, y, h - 1), dis[v]));
			if (y != m && dis[v = id[x][y + 1][h - 1]] > dis[c_id])
				dis[v] = dis[c_id], q.push(node(point(x, y + 1, h - 1), dis[v]));
		} else {
			if (dis[v = id[x][y][std::min(a[x][y], max)]] > dis[c_id] + b[x][y])
				dis[v] = dis[c_id] + b[x][y], 
					q.push(node(point(x, y, std::min(a[x][y], max)), b[x][y]));
		}
	}
}

inline void solve() {
	static long long d[4][4], ans = INF;
	char c;
	point t1 = point(x[1], y[1], 0);
	point t2 = point(x[2], y[2], 0);
	point t3 = point(x[3], y[3], 0);
	dijkstra(t1, t2, t3), d[1][2] = dis[get_id(t2)], d[1][3] = dis[get_id(t3)];
	dijkstra(t2, t1, t3), d[2][1] = dis[get_id(t1)], d[2][3] = dis[get_id(t3)];
	dijkstra(t3, t1, t2), d[3][1] = dis[get_id(t1)], d[3][2] = dis[get_id(t2)];
	if (d[2][1] + d[3][1] < ans) c = 'X', ans = d[2][1] + d[3][1];
	if (d[1][2] + d[3][2] < ans) c = 'Y', ans = d[1][2] + d[3][2];
	if (d[1][3] + d[2][3] < ans) c = 'Z', ans = d[1][3] + d[2][3];
	(ans == INF) ? std::cout << "NO" : std::cout << c << '\n' << ans;
}

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值