[IOI2013]wombats

题目链接

博客园链接

主要是想记录一下Maze 2D这种模型。

给一张 (n imes m) 的网格图,长成长条状,网格上的边有边权。人每次只能向上向下向右走,不能向左走。(q) 次询问,每次查从一个点走到另一个点的最短路。询问之间可能会穿插 (t) 次修改边权。

法一:DP

单次询问显然可以 (n imes m) 地DP:设 (f(i,j)) 表示从起点走到 ((i,j)) 的最短路。(g(i,j) = f(i,j-1) + lft(i,j))。((lft,rgt,up,dn) 均为边权)然后有:

[f(i,j) = min_k(g(k,j) + dis((k,j),(i,j)) ]

一列一列地考虑即可。复杂度:(O(t + nmq))

法二:线段树套Floyd

因为 (m) 非常小,我们可以用线段树维护 (L...R) 列中 (dis(i,j))。显然可以类似 Floyd 算法那样合并。这样的话复杂度就是 (O((t+q)log n imes m^3))。可以通过Maze 2D这道题。

法三:决策单调性优化Floyd

如果 (m) 不是那么小,我们每次 Floyd 的时候无法 (m^3) 地计算 (dis(i,j))。不过我们发现,((i,j)) 的决策点一定比 ((i,j-1))((i-1,j)) 靠后(假设边权均为正)。于是可以用决策单调性优化Floyd。复杂度:(O((t+q)log n imes m^2))。优化空间后可以通过[IOI2013]wombats这题。

(IOI2013wombats的代码)

const int block = 20;
int n, m;
int rgt[NN][N], dn[NN][N];
int g[N][N], h[N];
struct node {
	int f[N][N];
	int l, r;
	node() { memset(f, 0x3f, sizeof(f)); l = r = 0; }
	node(int L, int R) {
		l = L, r = R;
		memset(f, 0x3f, sizeof(f));
		memset(h, 0, sizeof(h));
		for (int p = 1; p <= m; ++p) {
			for (int sum = 0, j = p; j; --j, sum += rgt[L][j])	f[p][j] = sum;
			for (int sum = 0, j = p; j <= m; sum += rgt[L][j], ++j)	f[p][j] = sum;
			for (int i = L + 1; i <= R + 1; ++i) {
				for (int j = 1; j <= m; ++j)	h[j] = f[p][j] + dn[i - 1][j], f[p][j] = inf;
				for (int mn = h[1], sum = 0, j = 1; j <= m; sum += rgt[i][j], ++j, MIN(mn, h[j] - sum))
					MIN(f[p][j], mn + sum);
				for (int mn = h[m], sum = 0, j = m; j; --j, sum += rgt[i][j], MIN(mn, h[j] - sum))
					MIN(f[p][j], mn + sum);
			}
		}
	}
}nd[NNN];
inline node operator +(const node &a, const node &b) {
	node res;
	memset(g, 0, sizeof(g));
	for (int i = m; i; --i) {
		for (int j = 1; j <= m; ++j) {
			int d = j == 1 ? 1 : g[i][j - 1];
			int u = i == m ? m : g[i + 1][j];
			for (int k = d; k <= u; ++k) {
				if (res.f[i][j] > a.f[i][k] + b.f[k][j]) {
					res.f[i][j] = a.f[i][k] + b.f[k][j];
					g[i][j] = k;
				}
			}
		}
	}
	res.l = a.l; res.r = b.r;
	return res;
}

int ls[NNN], rs[NNN], ttot, root;
inline void pushup(int cur) {
	nd[cur] = nd[ls[cur]] + nd[rs[cur]];
}
void build(int L, int R, int &cur) {
	cur = ++ttot;
	if (R - L + 1 <= block)	return nd[cur] = node(L, R), void();
	int mid = (L + R) >> 1;
	build(L, mid, ls[cur]); build(mid + 1, R, rs[cur]);
	pushup(cur);
}
void modify(int L, int R, int pos, int cur) {
	if (R - L + 1 <= block)	return nd[cur] = node(L, R), void();
	int mid = (L + R) >> 1;
	if (pos <= mid)	modify(L, mid, pos, ls[cur]);
	else	modify(mid + 1, R, pos, rs[cur]);
	pushup(cur);
}
原文地址:https://www.cnblogs.com/JiaZP/p/13787363.html