LOJ #2022. 「AHOI / HNOI2017」队长快跑 (单调队列,转化思想,计算几何)

题目

题意:平面上有 n n 条射线,求从 ( 0 , 0 ) (0,0) ( T x , T y ) (Tx,Ty) 最短合法路径长度(保证存在合法路径),合法路径是指不经过射线(可以经过不被其他射线覆盖的端点)的,任意时刻走的方向在 x x 轴正方向上的投影为正的一条路径。

题解:
首先,可以发现对于每条射线,我们都是从下或从上绕过去,那么可以把每条射线调整为竖直向上或向下。
然后我们想到可以 d p dp ,发现除了起点和终点,我们只会在射线的端点处拐弯。
可以从左到右 d p dp i i 转移到 j j 的条件是中间不能有射线挡。

这东西可以单调队列优化,尽管我们还没有提出来怎么 d p dp
在这里插入图片描述

我们用两个单调队列来维护,一个维护起点+向上的,一个维护起点+向下的。
那么这个样子显然对于上面的每一点,他的最优决策点就是上面的前面一个点。
下面同理。
如果出现了一边(上或下)的最后3条射线,中间那条无法挡住左右射线的端点互相直达,那么中间右边射线的最优决策点就不可能是中间那条,于是我们弹出中间那条。
但是这样的维护没有考虑不同方向的射线互相遮挡影响的情况。
我们在插入一条射线 i i 时(不妨设其方向为向上),先向其射的反方向的队列中做如下判断:反方向的起点(队列中的队首)到这个射线端点的线段是否被队列中第二条射线截断。
如果不是,那么我们可以继续上下相安无事各自继续维护两种方向的单调队列。
如果是,那么我们的最优决策点就不可能是一条方向向上的线段。
这个。在上面的图中臆想一下。
那么之后的方向向上的射线 j j ,要么和起点的线段被 i i 截断,要么,反方向的起点到 j j 的端点的线段一定被队列中第二条射线截断,根据我们上面的断言,它的最优决策点也不可能是一条方向向上的线段。
之后的方向向下的射线,可以想想。
总之我们可以把 i i 之前的向上的线段全部从队列中删去。

我们在反方向,考虑如果反方向的起点到 i i 的线段被队列中第二条射线截断,则弹出队首。

直到最后我们的队首 k k 是可以到 i i 的。
那么之前被弹出的队首也可以被论证之后是不能作为最优决策点的。
这时 k k 相当于就是我们新起点,把它作为向上的队首,再把 i i 作为向上的队列中的第二个就行了。

中间有很多论证过程我都咕咕咕了,网上也没有一篇博客说了这个做法的正确性,所以可能是错的,我只是各位一种感性理解的方式。按理说这题和今年CSP-S
D2T2某p题解题方法好像啊,先猜结论后单调队列。。。,提高+警告

A C   C o d e \mathrm {AC \ Code}

#include<bits/stdc++.h>
#define LL long long
#define maxn 1000006
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
using namespace std;

int n,m;
struct Point{
	LL x,y;int dir;Point(LL x=0,LL y=0):x(x),y(y){}
	Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
	LL operator *(const Point &B)const{ return x*B.y-y*B.x; }
}a[maxn],b[maxn],S,T;
int dir[maxn],nd[maxn],c[maxn],pre[maxn];
bool cmp(const int &u,const int &v){ return a[u].x<a[v].x; }
int q[2][maxn],ql[2],qr[2];
double sqr(double a){ return a*a; }

int main(){
	scanf("%d%lld%lld",&n,&T.x,&T.y);
	rep(i,1,n){
		double l,r,t;
		scanf("%lld%lld%lf",&a[i].x,&a[i].y,&t);
		l=atan2(S.y-a[i].y,S.x-a[i].x);
		r=atan2(T.y-a[i].y,T.x-a[i].x);
		if(l<r) dir[i]=(l<t&&t<r);//1 dn 0 up
		else dir[i]=!(r<t&&t<l);
		c[i]=i;
	}
	sort(c+1,c+1+n,cmp);
	b[++m]=S;
	for(int i=1;i<=n && a[c[i]].x<T.x;i++)
		if(a[c[i]].x>S.x)
			b[++m]=a[c[i]],nd[m]=dir[c[i]];
	b[++m]=T;
	q[0][0]=q[1][0]=1;
	rep(i,1,m){
		int x=nd[i],&l0=ql[x],&r0=qr[x],&l1=ql[x^1],&r1=qr[x^1],*q0=q[x],*q1=q[x^1],tg=(x?-1:1);
		if(l1<r1 && (b[i]-b[q1[l1]])*(b[q1[l1+1]]-b[q1[l1]])*tg>=0){
			for(;l1<r1 && (b[i]-b[q1[l1]])*(b[q1[l1+1]]-b[q1[l1]])*tg>=0;l1++);
			pre[i]=q1[l1];q0[l0=r0=r0+1]=q1[l1];
		}
		else{
			for(;l0<r0 && (b[i]-b[q0[r0-1]])*(b[q0[r0]]-b[q0[r0-1]])*tg>=0;r0--);
			pre[i]=q0[r0];
		}
		q0[++r0]=i;
	}
	double ans = 0;
	for(int i=m;i!=1;i=pre[i]) ans+=sqrt(sqr(b[i].x-b[pre[i]].x)+sqr(b[i].y-b[pre[i]].y));
	printf("%.10lf\n",ans);
}

发布了627 篇原创文章 · 获赞 91 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/103682582
今日推荐