ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷 P3350 - [ZJOI2016]旅行者(分治+最短路+复杂度分析)

2022-07-07 14:34:39  阅读:122  来源: 互联网

标签:洛谷 log int dfrac ZJOI2016 mid l1 P3350 id


洛谷题面传送门

肿么没有人证明复杂度,那我来证一个。

考虑分治,每次像猫树那样处理一个分治区间 \([l_x,r_x],[l_y,r_y]\) 表示当前处理 \(x_1,x_2\in[l_x,r_x]\),\(y_1,y_2\in[l_y,r_y]\) 范围内的所有询问。处理当前层的询问是好办的,考虑令 \(mid=\lfloor\dfrac{l_x+r_x}{2}\rfloor\),那么我们对于 \(y\in[l_y,r_y]\),我们一遍 dijkstra 求出 \((mid,y)\) 到所有 \((X,Y)\) 的最短距离(\(X\in[l_x,r_x],Y\in[l_y,r_y]\)),然后对于所有询问 \((x_1,y_1,x_2,y_2)\) 我们用 \(dis_{x_1,y_1}+dis_{x_2,y_2}\) 更新答案。然后递归处理剩余询问——跨中线的询问就扔了不管了,全在左部的询问就扔到左边,全在右部的询问就扔到右边依次递归。

如果每次都劈 \([l_x,r_x]\) 的话复杂度显然是错误的,考虑一个浅显的优化:每次像 KDT 那样取区间长度较大的那一维劈开。如果你尝试实现这样的暴力你会发现你获得了 100 分的好成绩。这是为什么呢?首先正确性显然,跨中线的询问显然必须经过某个 \((mid,y)\),因此此轮更新已经包含了所有的情况,不用进一步递归。而全在左部的询问,我们已经求出了它到中间某个点然后折返的最小代价,此时如果它绕到右部,那么它必然经过中线,这种情况我们也已经计算过了,故该算法的正确性是没有问题的。

接下来考虑复杂度,注意到一轮更新的复杂度是 \(nm\min(n,m)\log nm+\min(n,m)·Q\),由于这个 \(\min\) 的存在,使复杂度达到最大值的图形形状肯定形如一个正方形(或者一个长宽比为 \(1:\sqrt{2}\) 的矩形懒得管了,总之大概就这个量级的,不会长宽相差过于悬殊,下面分析就以正方形来推),而后面的 \(\min(n,m)·Q\) 只与询问递归的深度有关,因此最劣情况肯定是询问递归到底,下面就用这种情况来分析复杂度。

设 \(T(n,Q)\) 表示边长为 \(n\) 的正方形有 \(Q\) 个询问的复杂度,\(T(n,m,Q)\) 表示 \(n\times m\) 的矩形有 \(Q\) 个询问的复杂度,那么有

\[T(n,Q)=n^3\log(n^2)+nq+T(\dfrac{n}{2},n,Q) \]

\[T(n,Q)=n^3\log(n^2)+nq+((\dfrac{n}{2})^2·n·\log(n^2)+\dfrac{n}{2}·Q)\times 2+4T(\dfrac{n}{2},\dfrac{Q}{4}) \]

\[T(n,Q)=\dfrac{3}{2}n^3\log(n^2)+2nQ+4T(\dfrac{n}{2},\dfrac{Q}{4}) \]

\[T(n,Q)=\sum\limits_{i=0}^{\log(n^2)}\dfrac{3}{2}·(\dfrac{n}{2^i})^3·4^i\log(n^2)+2·\dfrac{n}{2^i}·\dfrac{Q}{4^i}·4^i \]

\[T(n,Q)=\sum\limits_{i=0}^{\log(n^2)}\dfrac{3}{2}·\dfrac{n^3}{2^i}·\log(n^2)+2·\dfrac{n}{2^i}·Q \]

显然 \(T(n,Q)=n^3\log(n^2)+nQ=A\sqrt{A}\log A+\sqrt{A}·Q\)。

其中 \(A=\max\{nm\}\)。

const int MAXV = 2e4;
const int MAXQ = 1e5;
const int INF = 0x3f3f3f3f;
int n, m, qu, res[MAXQ + 5]; pii rid[MAXV + 5];
struct qry {int x1, y1, x2, y2;} q[MAXQ + 5];
int hd[MAXV + 5], to[MAXV * 4 + 5], nxt[MAXV * 4 + 5], val[MAXV * 4 + 5], ec = 0;
int getid(int x, int y) {return (x - 1) * m + y;}
void adde(int u, int v, int w) {to[++ec] = v; val[ec] = w; nxt[ec] = hd[u]; hd[u] = ec;}
int dis[MAXV + 5];
void dijkstra(int l1, int r1, int l2, int r2, int X, int Y) {
	if (dis[getid(X, Y)] != INF) {
		for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++)
			if (i != X || j != Y) dis[getid(i, j)] += dis[getid(X, Y)];
	}
	dis[getid(X, Y)] = 0;
	priority_queue<pii, vector<pii>, greater<pii> > q;
	q.push(mp(0, getid(X, Y)));
	while (!q.empty()) {
		pii p = q.top(); q.pop(); int x = p.se;
		for (int e = hd[x]; e; e = nxt[e]) {
			int y = to[e], z = val[e];
			if (l1 <= rid[y].fi && rid[y].fi <= r1 && l2 <= rid[y].se && rid[y].se <= r2) {
				if (dis[y] > dis[x] + z) dis[y] = dis[x] + z, q.push(mp(dis[y], y));
			}
		}
	}
}
void solve(int l1, int r1, int l2, int r2, vector<int> cd) {
	if (l1 == r1 && l2 == r2) {
		for (int id : cd) res[id] = 0;
		return; 
	}
	if (r1 - l1 + 1 > r2 - l2 + 1) {
		int mid = l1 + r1 >> 1;
		for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++) dis[getid(i, j)] = INF;
		for (int i = l2; i <= r2; i++) {
			dijkstra(l1, r1, l2, r2, mid, i);
			for (int id : cd) chkmin(res[id], dis[getid(q[id].x1, q[id].y1)] + dis[getid(q[id].x2, q[id].y2)]);
		}
		vector<int> L, R;
		for (int id : cd) {
			if (max(q[id].x1, q[id].x2) <= mid) L.pb(id);
			if (min(q[id].x1, q[id].x2) > mid) R.pb(id);
		}
		solve(l1, mid, l2, r2, L); solve(mid + 1, r1, l2, r2, R);
	} else {
		int mid = l2 + r2 >> 1;
		for (int i = l1; i <= r1; i++) for (int j = l2; j <= r2; j++) dis[getid(i, j)] = INF;
		for (int i = l1; i <= r1; i++) {
			dijkstra(l1, r1, l2, r2, i, mid);
			for (int id : cd) chkmin(res[id], dis[getid(q[id].x1, q[id].y1)] + dis[getid(q[id].x2, q[id].y2)]);
		}
		vector<int> L, R;
		for (int id : cd) {
			if (max(q[id].y1, q[id].y2) <= mid) L.pb(id);
			if (min(q[id].y1, q[id].y2) > mid) R.pb(id);
		}
		solve(l1, r1, l2, mid, L); solve(l1, r1, mid + 1, r2, R);
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++)
		rid[getid(i, j)] = mp(i, j);
	for (int i = 1; i <= n; i++) for (int j = 1; j < m; j++) {
		int x; scanf("%d", &x);
		adde(getid(i, j), getid(i, j + 1), x);
		adde(getid(i, j + 1), getid(i, j), x);
	}
	for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++) {
		int x; scanf("%d", &x);
		adde(getid(i, j), getid(i + 1, j), x);
		adde(getid(i + 1, j), getid(i, j), x);
	}
	scanf("%d", &qu); memset(res, 63, sizeof(res));
	for (int i = 1; i <= qu; i++) scanf("%d%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2, &q[i].y2);
	vector<int> all; for (int i = 1; i <= qu; i++) all.pb(i);
	solve(1, n, 1, m, all);
	for (int i = 1; i <= qu; i++) printf("%d\n", res[i]);
	return 0;
}

标签:洛谷,log,int,dfrac,ZJOI2016,mid,l1,P3350,id
来源: https://www.cnblogs.com/ET2006/p/luogu-P3350.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有