P6007 [USACO20JAN]Springboards G

P6007 [USACO20JAN]Springboards G

题意

你从 (0,0) 出发,到达 (n,n) ,每次只能向上或向右走,有 m 个传送门,将你传送到传送门起点右上方的一个终点。求最少走路次数。

解法

我们不走传送门的时候答案就是 2n ,走了一个传送门从 (sx,sy)(ex,ey) 后,答案就变成 2n[(exsx)+(eysy)]

我们把每个传送门看做一个点,把终点和起点的曼哈顿距离看作点权,答案就是 2nmax()

f[i] 表示到达第 i 个传送门时的点权最大值,状态转移方程: f[i]=max(f[i],f[j]+val[i]),ex[j]<=sx[i],ey[j]<=sy[i]

两维的限制条件,显然可以用树状数组维护 y 的值,我们需要保证更新 i 时,全部且仅有 ex[j]<=sx[i] 的点 j 被扔入树状数组。

我的第一思路是双指针,用 i 表示更新到第几个值,用 j 表示扔进树状数组的值到了第几个,但普通的双指针是不行的,因为 exsx 不一定同时单调递增。

所以正确的解法是:开两个数组,一个按照 sx 排序,用来表示求答案的顺序,另一个按照 ex 排序,用来丢进树状数组,可以证明,每次用到第二个数组的点时,它已经被更新过了。

解题过程

刚开始把一堆 m 写成 n ,一直错,然后因为使用第一思路只有 46pts ,用第一篇题解对拍出来的结果令我困扰许久,然后我又用第二篇,第三篇,最后发现题解很多都有问题。辗转半天,终于通过了这道题。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int read(){
	int x=0,f=1;char c=getchar();
	while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
struct POS{
	int sx,sy,ex,ey,val,id;
}p[N],p2[N];
int n,m,C[N*3];
int lshx[N*2],lshy[N*2];
int mx,my,dis[N];
bool cmp(POS A,POS B){
	return A.ex==B.ex?A.ey<B.ey:A.ex<B.ex;
}
bool cmp2(POS A,POS B){
	return A.sx==B.sx?A.sy<B.sy:A.sx<B.sx;
}
void update(int u,int x){
	for(int i=u;i<=my;i+=i&(-i))C[i]=max(C[i],x);
	return;
}
int query(int u){
	int res=0;
	for(int i=u;i>0;i-=i&(-i))res=max(res,C[i]);
	return res;
}
void LSH(){
	sort(lshx+1,lshx+2*m+1);
	sort(lshy+1,lshy+2*m+1);
	mx=unique(lshx+1,lshx+2*m+1)-lshx-1;
	my=unique(lshy+1,lshy+2*m+1)-lshy-1;
	for(int i=1;i<=m;i++){
		p[i].sx=lower_bound(lshx+1,lshx+mx+1,p[i].sx)-lshx;
		p[i].ex=lower_bound(lshx+1,lshx+mx+1,p[i].ex)-lshx;
		p[i].sy=lower_bound(lshy+1,lshy+my+1,p[i].sy)-lshy;
		p[i].ey=lower_bound(lshy+1,lshy+my+1,p[i].ey)-lshy;
	}
	return;
}
signed main(){
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		p[i].id=i;
		p[i].sx=read(),p[i].sy=read();
		lshx[2*i-1]=p[i].sx,lshy[2*i-1]=p[i].sy;
		p[i].ex=read(),p[i].ey=read();
		lshx[2*i]=p[i].ex,lshy[2*i]=p[i].ey;
		p[i].val=(p[i].ex-p[i].sx)+(p[i].ey-p[i].sy);
	}
	LSH();
	for(int i=1;i<=m;i++)p2[i]=p[i];
	sort(p+1,p+m+1,cmp);
	sort(p2+1,p2+m+1,cmp2);
	for(int i=1,j=1;i<=m;i++){
		while(j<i && p[j].ex<=p2[i].sx){update(p[j].ey,dis[p[j].id]);j++;}
		dis[p2[i].id]=query(p2[i].sy)+p2[i].val;
	}
	int ans=0;
	for(int i=1;i<=m;i++)ans=max(ans,dis[i]);
	printf("%lld\n",n*2-ans);
	return 0;
} 
/*
3 5
2 3 2 5
4 0 9 2
0 0 1 3
5 2 10 4
4 0 4 0

5 5
2 3 5 4
1 4 5 8
2 3 2 3
1 2 3 6
0 1 1 5

5 5
1 3 1 4
0 1 0 1
0 0 1 1
1 0 1 0
1 2 1 3

*/
posted @   FJOI  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示