首页 > 其他分享 >P3350 [ZJOI2016] 旅行者

P3350 [ZJOI2016] 旅行者

时间:2024-05-09 20:57:02浏览次数:23  
标签:P3350 int mid 旅行者 que ZJOI2016 y1 id dis

P3350 [ZJOI2016] 旅行者

分治+最短路

网格图可以想到分治。每次将长边分为两半,处理越过中线的询问。那么就可以枚举中线上的每个点更新答案,经过 \(x\) 的路径更新 \((u,v)\) 就是 \(dis_{u,x}+dis_{x,v}\)。每次预处理中线上每个点的单源最短路即可。

设 \(S=nm\),复杂度 \(O(S\sqrt{S}\log S)\)。

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e4 + 10, M = 1e5 + 10;
int n, m, Q;
int id(int x, int y) {return (x - 1) * m + y;}
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int c[N], r[N], w[N][4], dis[N], vis[N];
struct node {
	int x1, x2, y1, y2, id;
} que[M], tmp[M];
int ans[M];
struct com {
	int x, y, w;
	friend bool operator < (com a, com b) {
		return a.w > b.w;
	}
} tp;
std::priority_queue<com> q;
void dij(int sx, int sy, int xl, int xr, int yl, int yr, int ql, int qr) {
	q.push({sx, sy, 0});
	for(int i = xl; i <= xr; i++) {
		for(int j = yl; j <= yr; j++) {
			dis[id(i, j)] = iinf;
			vis[id(i, j)] = 0;
		}
	}
	dis[id(sx, sy)] = 0;
	while(!q.empty()) {
		com nw = q.top();
		q.pop();
		int u = id(nw.x, nw.y);
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = 0; i < 4; i++) {
			int tx = nw.x + dx[i], ty = nw.y + dy[i];
			if(tx < xl || tx > xr || ty < yl || ty > yr) continue;
			int v = id(tx, ty);
			if(dis[v] > dis[u] + w[u][i]) {
				dis[v] = dis[u] + w[u][i];
				tp = {tx, ty, dis[v]};
				q.push(tp);
			}
		}
	}
	for(int i = ql; i <= qr; i++) {
		ans[que[i].id] = std::min(ans[que[i].id], dis[id(que[i].x1, que[i].y1)] + dis[id(que[i].x2, que[i].y2)]);
	}
}
void solve(int x1, int x2, int y1, int y2, int l, int r) {
	if(x2 - x1 >= y2 - y1) {
		int mid = (x1 + x2) >> 1;
		for(int i = y1; i <= y2; i++) dij(mid, i, x1, x2, y1, y2, l, r);
		for(int i = l; i <= r; i++) tmp[i] = que[i];
		int ls = l - 1, rs = r + 1;
		for(int i = l; i <= r; i++) {
			if(tmp[i].x1 < mid && tmp[i].x2 < mid) que[++ls] = tmp[i];
			if(tmp[i].x1 > mid && tmp[i].x2 > mid) que[--rs] = tmp[i];
		} 
		if(ls >= l) solve(x1, mid - 1, y1, y2, l, ls);
		if(rs <= r) solve(mid + 1, x2, y1, y2, rs, r);
	} else {
		int mid = (y1 + y2) >> 1;
		for(int i = x1; i <= x2; i++) dij(i, mid, x1, x2, y1, y2, l, r);
		for(int i = l; i <= r; i++) tmp[i] = que[i];
		int ls = l - 1, rs = r + 1;
		for(int i = l; i <= r; i++) {
			if(tmp[i].y1 < mid && tmp[i].y2 < mid) que[++ls] = tmp[i];
			if(tmp[i].y1 > mid && tmp[i].y2 > mid) que[--rs] = tmp[i];
		} 
		if(ls >= l) solve(x1, x2, y1, mid - 1, l, ls);
		if(rs <= r) solve(x1, x2, mid + 1, y2, rs, r);
	}
}
void Solve() {
	std::cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j < m; j++) {
			std::cin >> r[id(i, j)];
		}
	}
	for(int i = 1; i < n; i++) {
		for(int j = 1; j <= m; j++) {
			std::cin >> c[id(i, j)];
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			for(int k = 0; k < 4; k++) {
				int x = i + dx[k], y = j + dy[k];
				if(x < 1 || x > n || y < 1 || y > m) continue;
				if(k == 0) w[id(i, j)][k] = c[id(i, j)];
				if(k == 1) w[id(i, j)][k] = c[id(i - 1, j)];
				if(k == 2) w[id(i, j)][k] = r[id(i, j)];
				if(k == 3) w[id(i, j)][k] = r[id(i, j - 1)]; 
			}
		}
	}

	std::cin >> Q;
	for(int i = 1; i <= Q; i++) {
		std::cin >> que[i].x1 >> que[i].y1 >> que[i].x2 >> que[i].y2;
		que[i].id = i;
	}
	memset(ans, 0x3f, sizeof(ans));
	solve(1, n, 1, m, 1, Q);

	for(int i = 1; i <= Q; i++) std::cout << ans[i] << "\n";
	return; 
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}

标签:P3350,int,mid,旅行者,que,ZJOI2016,y1,id,dis
From: https://www.cnblogs.com/FireRaku/p/18183045

相关文章

  • 旅行者
    新方法get法一:我们考虑最终的答案,一定是从某一个关键点\(A\)走到另一个关键点\(B\),那我们要找一种最短路径,保证中途经过两个关键点,而且能够覆盖所有的关键点对。所以我们考虑把其中一部分关键点作为起始点的下一个点,剩下的关键点作为终点的上一个点,于是我们建立两个虚点\(s\)......
  • P5304 [GXOI/GZOI2019] 旅行者
    Mikuuu准备投身于ACM的潮流中,失踪人口回归啦!这个题目的思路还是非常有趣的,因为我们可以注意到,两个可能成为答案兴趣点之间的最短路不应该经过了第三个点,如果经过了,显然和第三个点之间的最短路会更小,则原来的两个点之间不应该成为答案。考虑到这一点,我们可以想到建枚举每一条边,......
  • 图论 - 某进制分组 - P5304 旅行者
    P5304旅行者\(\mathtt{TAGS}\):多源多汇最短路,二进制分组\(\mathtt{ESTIMATION}\):非常好二进制分组,让我的大脑旋转题意简述给定\(k\)个点和一张有向图,求以这\(k\)个点为起点和终点的最短路中最短的一条的长度。First.怎么求多源多汇最短路solution.1超级源点和超级......
  • P3352 [ZJOI2016] 线段树 思考--zhengjun
    有一个显然的\(O(n^3q)\)的做法:设\(f_{i,l,r,x}\)表示\(i\)次操作过后,区间\([l,r]\)的数\(\lex\),\(a_{l-1},a_{r+1}>x\)的方案数。转移:$$f_{i,l,r,x}=f_{i-1,l,r,x}\timesg_{l,r}+\sum\limits_{j<l}f_{i-1,j,r,x}\times(j-1)+\sum\limits_{j>r}f_{i-1,l......
  • COMP3350 业务智能
    Assignment2–BusinessIntelligenceSemester1,2023DueDateApr24th5pmAssignment2isdueonApr24th5pm.Eachgroupwill•uploadtheassignmentfilestoCanvasand•presentyourBIreportanddemonstrateyourassignmentontutorialsessiononApr25th......
  • ZJOI2016 小星星
    标签:子集反演,动态规划[ZJOI2016]小星星题目描述小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有\(n\)颗小星星,用\(m\)条彩色的细线串了起来,每条细线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了\(n-1\)条细线,但通......
  • [ZJOI2016] 小星星
    [ZJOI2016]小星星题目描述小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有\(n\)颗小星星,用\(m\)条彩色的细线串了起来,每条细线连着两颗小星星。有一天......
  • 算法--旅行者过河问题
    1.题目在漆黑的夜里,四位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,四个人一共只带了一只手电筒,而桥窄得只够......
  • [dp 记录]P3349 [ZJOI2016]小星星
    绝世容斥好题,刚好NOIp前要复习容斥,就拉过来当100紫了。祝自己明天的NOIprp++这题好久前看过题解,感觉好可惜,浪费了好题。以后自己不会的题也不能看题解了。题意:......