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

P3350 [ZJOI2016] 旅行者

时间:2024-07-03 09:44:24浏览次数:22  
标签:fi frac xl int 旅行者 yy ZJOI2016 P3350 yr

咕了2天才写的题解

还是比较经典的题目,分治处理网格图最短路

离线下来,利用分治的思想,用一条线把网格图平均劈成两半,每次只考虑询问在两块的一对点,所有的线必须经过直线上的一个点,于是我把线上所有点都在规定范围内跑一次dijkstra,最后直接算答案,显然我想让最短路跑的次数最小,每次选较短的边作为线段的长度,从较高的边上劈开,如果一组询问在同一块,分别递归,就是子问题了。这个做法正确性显然。

时间复杂度证明如下:

设点数有\(|V|\)个,当前分治在第\(k\)轮,那么线的长度就最多是\(\sqrt{\frac{|V|}{2^k}}\),要进行线的长度次的dijkstra,因为网格图点数与边数同级,所以每次dijkstra时间复杂度是\(O(\frac{|V|}{2^k}\log\frac{|V|}{2^k})\),即\(O(\frac{|V|}{2^k}\log{|V|})\),然后第\(k\)轮,要分治\(2^k\)次,时间复杂度是\(O(2^k\cdot\sqrt{\frac{|V|}{2^k}}\frac{|V|}{2^k}\log{|V|})\),整理得\(\frac{|V|^{\frac{3}{2}}\cdot \log{|V|}}{2^{\frac{k}{2}}}\),有\(k\)轮,分母相加,由等比数列求和公式,知道是个常数,如果我没算错的话就是\(\sqrt{2}+1\),然后时间复杂度就是\(O(|V|^{\frac{3}{2}}\cdot \log{|V|})\),这道题\(|V|\)最大是2e4,这么算下来最多也只有4e7级别

#include<bits/stdc++.h>
#define vd void 
#define MAXN 200005 
#define pr std::pair<int,int>
#define fi first
#define se second 
const int inf=1e9;
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
template<class T>vd cnk(T&a,T b){a=a<=b?a:b;}
struct Q{
	int pos,st,ed;
}query[MAXN],g[MAXN],g2[MAXN];
int n,m,q,ans[MAXN],x[MAXN],y[MAXN],dis[MAXN];
bool vis[MAXN];
std::vector<pr>nbr[MAXN];
struct node{
	int to,W;
	bool friend operator<(node a,node b){return a.W>b.W;}
};
std::priority_queue<node>pq;
int id(int xx,int yy){return (xx-1)*m+yy;}
vd add(int xx,int yy,int w){nbr[xx].emplace_back(yy,w),nbr[yy].emplace_back(xx,w);}
vd dijkstra(int s,int xl,int xr,int yl,int yr){
	for(int i=xl;i<=xr;i++)for(int j=yl;j<=yr;j++)dis[id(i,j)]=inf,vis[id(i,j)]=0;
	dis[s]=0;pq.push({s,0});
	while(!pq.empty()){
		int u=pq.top().to;pq.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(pr v:nbr[u]){
			if(x[v.fi]<xl||x[v.fi]>xr||y[v.fi]<yl||y[v.fi]>yr)continue;
			if(dis[v.fi]>dis[u]+v.se)dis[v.fi]=dis[u]+v.se,pq.push({v.fi,dis[v.fi]});
		}
	}
}
vd solve(int ql,int qr,int xl,int xr,int yl,int yr){
	if(ql>qr||xl>xr||yl>yr)return;
	if(xr-xl>=yr-yl){
		int mid=(xl+xr)>>1,lg=0,lg2=0;
		for(int i=yl;i<=yr;i++){
			dijkstra(id(mid,i),xl,xr,yl,yr);
			for(int j=ql;j<=qr;j++)cnk(ans[query[j].pos],dis[query[j].st]+dis[query[j].ed]);
		}
		for(int i=ql;i<=qr;i++){
			if(x[query[i].st]<mid&&x[query[i].ed]<mid)g[++lg]=query[i];
			else if(x[query[i].st]>mid&&x[query[i].ed]>mid)g2[++lg2]=query[i];
		}
		for(int i=1;i<=lg;i++)query[ql+i-1]=g[i];
		for(int i=1;i<=lg2;i++)query[ql+lg+i-1]=g2[i];
		solve(ql,ql+lg-1,xl,mid-1,yl,yr),solve(ql+lg,ql+lg+lg2-1,mid+1,xr,yl,yr);
	}else{
		int mid=(yl+yr)>>1,lg=0,lg2=0;
		for(int i=xl;i<=xr;i++){
			dijkstra(id(i,mid),xl,xr,yl,yr);
			for(int j=ql;j<=qr;j++)cnk(ans[query[j].pos],dis[query[j].st]+dis[query[j].ed]);
		}
		for(int i=ql;i<=qr;i++){
			if(y[query[i].st]<mid&&y[query[i].ed]<mid)g[++lg]=query[i];
			else if(y[query[i].st]>mid&&y[query[i].ed]>mid)g2[++lg2]=query[i];
		}
		for(int i=1;i<=lg;i++)query[ql+i-1]=g[i];
		for(int i=1;i<=lg2;i++)query[ql+lg+i-1]=g2[i];
		solve(ql,ql+lg-1,xl,xr,yl,mid-1),solve(ql+lg,ql+lg+lg2-1,xl,xr,mid+1,yr);
	}
}
int main(){
	n=gi(),m=gi();
	for(int i=1;i<=n;i++)for(int j=1;j<m;j++){int w=gi();add(id(i,j),id(i,j+1),w);};
	for(int i=1;i<n;i++)for(int j=1;j<=m;j++){int w=gi();add(id(i,j),id(i+1,j),w);};
	q=gi();
	for(int i=1;i<=q;i++){
		int a=gi(),b=gi(),c=gi(),d=gi();
		query[i]={i,id(a,b),id(c,d)},ans[i]=inf;
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)x[id(i,j)]=i,y[id(i,j)]=j;
	solve(1,q,1,n,1,m);
	for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
	return 0;
}

算是一个套路吧,网格图最短路想分治

标签:fi,frac,xl,int,旅行者,yy,ZJOI2016,P3350,yr
From: https://www.cnblogs.com/xiaboxin/p/18280974

相关文章

  • P3349 [ZJOI2016] 小星星
    P3349[ZJOI2016]小星星树形dp+子集反演有一张图和一棵树,点数都为\(n\),给树上的每个点一个映射\(a_i\),每个\(a_i\)不同,\(a_i\in[1,n]\)。要求对于树上所有\((u,v)\),都有\((a_u,a_v)\)在图上。求映射方案数。看到\(n\)的范围,可以想到树形状压dp。设\(F_{i,j,S}\)......
  • P3350 [ZJOI2016] 旅行者
    P3350[ZJOI2016]旅行者分治+最短路网格图可以想到分治。每次将长边分为两半,处理越过中线的询问。那么就可以枚举中线上的每个点更新答案,经过\(x\)的路径更新\((u,v)\)就是\(dis_{u,x}+dis_{x,v}\)。每次预处理中线上每个点的单源最短路即可。设\(S=nm\),复杂度\(O(S\sq......
  • 旅行者
    新方法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.题目在漆黑的夜里,四位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,四个人一共只带了一只手电筒,而桥窄得只够......