首页 > 其他分享 >[ABC286Ex] Don't Swim 题解

[ABC286Ex] Don't Swim 题解

时间:2024-02-27 22:13:36浏览次数:14  
标签:Swim ld ABC286Ex point 题解 st && rightarrow define

我们首先求出线段与多边形的交点,如果交点个数 \(<2\) 或者有无数个交点,则可以直接输出 \(S\) 到 \(T\) 之间的距离。

接下来我们考虑交点个数为 \(2\) 的情况。

为了方便,我们记距离 \(S\) 最近的那个交点为 \(P_1\),远的为 \(P_2\)。

举个例子:

在这个例子中,直线 \(ST\) 将整个多边形分成了两个凸壳,我们可以从多边形下面过去也可以从上面过去。考虑对于两部份分别求出一条最短路径再取一个最小值。(这里上半部分指的是向量 \(\overrightarrow{ST}\) 的左半平面)

我们先考虑上半部分的一条路径 \(S\rightarrow P_1 \rightarrow A_1\rightarrow A_6 \rightarrow A_5 \rightarrow P_2 \rightarrow T\),可以发现这条路径的一个前缀 \(S\rightarrow P_1 \rightarrow A_1\) 没有 \(S\rightarrow A_1\) 更优(三角形的性质)。

考虑按到 \(S\) 的距离的顺序枚举上半部分的所有点。我们维护一个栈,每次新加入一个点时,如果栈的大小 \(<2\),就直接入栈。

对于其它情况,我们记当前这个点为 \(P\),栈顶的两个点分别为 \(A,B\)(\(A\) 比 \(B\) 先入栈),考虑向量 \(\overrightarrow{AB}\) 与向量 \(\overrightarrow{BP}\) 的叉积,如果符号不为则将 \(B\) 弹出栈,重复这个操作直到符号不为负或者栈的大小 \(<2\)。最后再将 \(P\) 入栈。

那么路径的长度就是最后的栈中相邻两个点的距离的和。对于下半部分也是类似的操作,但是在判断叉积的符号时如果符号不为才将 \(B\) 弹出栈。

那么剩下的问题就是如何求出两半部份的点了。我们记 \(P_1\) 所在的线段的两个端点分别是 \(A_i,A_{(i+1)\mod n}\)(如果存在多条线段则任取一个),\(P_2\) 所在的线段的两个端点分别是 \(A_j,A_{(j+1)\mod n}\),那么点 \(A_{i+1},A_{i+2},\dots,A_{n-1},A_0,A_1,\dots A_j\) 属于下半部分,剩下的就属于上半部分。

代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define md 1000000007
#define pb push_back
#define mkp make_pair
#define ld long double
#define umap unordered_map
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
#define pq priority_queue
using namespace std;
struct point{
	ld x,y;
	inline void in(){
		scanf("%Lf%Lf",&x,&y);
	}
}s,t,a[mxn],q[mxn+10];
struct line{
	ld k,b;
	line(point x,point y){
		k=(y.y-x.y)/(y.x-x.x);
		b=x.y-x.x*k;
	}
	inline ld at(ld x){
		return x*k+b;
	}
};
const ld esp=1e-8;
inline point operator-(point x,point y){
	return {x.x-y.x,x.y-y.y};
}
inline bool operator==(point x,point y){
	return fabs(x.x-y.x)<esp&&fabs(x.y-y.y)<esp;
}
inline bool operator==(line x,line y){
	return fabs(x.k-y.k)<esp&&fabs(x.b-y.b)<esp;
}
inline point operator&(line a,line b){
	ld x=(b.b-a.b)/(a.k-b.k);
	return {x,a.k*x+a.b};
}
inline ld operator*(point x,point y){
	return x.x*y.y-x.y*y.x;
}
inline int cmp(ld x){
	if(fabs(x)<esp)return 0;
	return x<0?-1:1;
}
inline ld len(point x){
	return sqrt(x.x*x.x+x.y*x.y);
}
int n,m;
vector<pair<point,int> >st,d1;
ld ans1,ans2;
inline void add1(point x){
	while(m>1&&(x==q[m]||cmp((q[m]-q[m-1])*(x-q[m]))!=1))m--;
	q[++m]=x;
}
inline void add2(point x){
	while(m>1&&(x==q[m]||cmp((q[m]-q[m-1])*(x-q[m]))!=-1))m--;
	q[++m]=x;
}
signed main(){
	scanf("%d",&n);
	rept(i,0,n)a[i].in();
	s.in(),t.in();
	line d(s,t);
	rept(i,0,n){
		line x(a[i],a[(i+1)%n]);
		if(s.x==t.x&&a[i].x==a[(i+1)%n].x){
			if(s.x==a[i].x){
				printf("%.10Lf",len(s-t));
				return 0;
			}
			continue;
		}
		if(s.x!=t.x&&x.k==d.k){
			if(x.b==d.b){
				printf("%.10Lf",len(s-t));
				return 0;
			}
			continue;
		}
		point p;
		if(s.x==t.x){
			p={s.x,x.at(s.x)};
		}else if(a[i].x==a[(i+1)%n].x){
			p={a[i].x,d.at(a[i].x)};
		}else p=x&d;
		if(p.x>=min(a[i].x,a[(i+1)%n].x)&&p.x<=max(a[i].x,a[(i+1)%n].x)&&p.x>=min(s.x,t.x)&&p.x<=max(s.x,t.x)&&p.y>=min(a[i].y,a[(i+1)%n].y)&&p.y<=max(a[i].y,a[(i+1)%n].y)&&p.y>=min(s.y,t.y)&&p.y<=max(s.y,t.y)){
			d1.pb(mkp(p,i));
		}
	}
	for(auto i:d1){
		for(auto j:st)if(i.first==j.first)goto next;
		st.pb(i);
		next:;
	}
	if(st.size()!=2){
		printf("%.10Lf",len(s-t));
		return 0;
	}
	if(len(s-st[0].first)>len(s-st[1].first))swap(st[0],st[1]);
	add1(s),add1(st[0].first);
	int i=(st[0].second+1)%n;
	while(1){
		add1(a[i]);
		if(i==st[1].second)break;
		i=(i+1)%n;
	}
	add1(st[1].first),add1(t);
	rep(i,2,m)ans1+=len(q[i]-q[i-1]);
	m=0;
	add2(s),add2(st[0].first);
	i=st[0].second;
	while(1){
		add2(a[i]);
		if(i==(st[1].second+1)%n)break;
		i=(i+n-1)%n;
	}
	add2(st[1].first),add2(t);
	rep(i,2,m)ans2+=len(q[i]-q[i-1]);
	printf("%.10Lf",min(ans1,ans2));
	return 0;
}

标签:Swim,ld,ABC286Ex,point,题解,st,&&,rightarrow,define
From: https://www.cnblogs.com/zifanoi/p/18038526

相关文章

  • P3577 [POI2014]TUR-Tourism 题解
    考虑在无向图上进行dfs,可以得到很多棵dfs树(因为图不一定连通),这些树形成了一个森林。然后由任意两点间不存在节点数超过\(10\)的简单路径这个限制可以得出这些树的深度都不超过\(10\),然后可以想到树上状压dp。有一个重要的性质,就是无向图dfs树上的非树边,一定是回边,所以......
  • CF516E Drazil and His Happy Friends 题解
    题目传送门记\(d=\gcd(n,m)\),发现只有编号在模\(d\)意义下相同的人之间会产生影响,那么有解当且仅当每个剩余系内有至少一个人是快乐的。所以在\(d>b+g\)时直接输出-1即可。对于剩下的情况,先令\(n\leftarrow\fracnd,m\leftarrow\fracmd\),如果\(n<m\)那么把男女交......
  • [ARC140F] ABS Permutation (Count ver.) 题解
    洛谷题面传送门AT题面传送门发现不太好直接求,考虑将\(P\)映射到\(P^{-1}\)上,这样题目中的条件就变成了\(|P_i-P_{i+M}|=1\)。因此我们可以对模\(M\)的每个剩余系做\(M=1\)的情况,然后最后快速幂合并。考虑\(M=1\)的情况怎么做。记\(f_i\)表示\(K=i\)的方案数,......
  • [ARC112F] Die Siedler 题解
    题目传送门人类智慧题。发现\(2\)操作肯定是不劣的,所以能换则换。考虑将手上的牌转换成一个多进制的数,这样\(2\)操作就是其进位方法,\(1\)操作就是将当前的数加上牌包对应的数,答案就是其各位数字之和,不难发现其值为:\[\sum_{i=1}^nc_i\times2^{i-1}\times(i-1)!\]再考......
  • 2024.2.27模拟赛T2题解
    题目有一个神奇的结论\(\foralla<b<c,a\oplusc>min(a\oplusb,b\oplusc)\)然后就可以写出\(n^2\)dp,再用TRIE树优化即可code#include<bits/stdc++.h>usingnamespacestd;#defineN200005#defineintlonglongintn,k1,k2;inta[N],fl[2];constintm......
  • CF1392I Kevin and Grid 题解
    题目传送门\(\large\textbf{Statement.}\)给定两个序列\(a,b\),有一个\(n\timesm\)的网格图,每个点\((i,j)\)上有个权值\(a_i+b_j\),每个点和其上、下、左、右方相邻的点有连边。多次询问,每次给一个阈值\(x\),将图分为权值\(<x\)(蓝色)和\(\gex\)(红色)的两种连通块。一......
  • P10084 [GDKOI2024 提高组] 计算 题解
    题目传送门前置知识:单位根反演先考虑怎么求\(F(x,a,b)\),易得\(\gcd(x^a-1,x^b-1)=x^{\gcd(a,b)}-1\)。所以\(L=m^{\gcd(a,b)}+1,R=m^{\gcd(c,d)}\),然后发现\([L,R]\)中的数模\(m\)后每种余数出现次数相同,下面记其为\(n=\frac{R-L}{m}\)。考虑用生成函数做,易得答案为......
  • [ABC331G] Collect Them All 题解
    洛谷传送门AT传送门\(\textbf{Statement.}\)有\(M\)种颜色,用\(1\simM\)编号,每次抽奖抽中第\(i\)种颜色的概率为\(\frac{c_i}{N}\),其中\(\sumc_i=N\),求抽中每种颜色至少一次的期望次数。\(1\leM\leN\le2\times10^5\)。\(\textbf{Solution.}\)发现直接求不好......
  • [ARC087F] Squirrel Migration 题解
    洛谷题面AT题面如果两条路径不存在交点,则两条路径各选一个端点交换后两路径相交,答案不会变劣。考虑所有路径两两相交的情况,因为图是一棵树,所以这些路径会交于一点。以这个点为根,那么最大的子树大小一定不超过\(\fracn2\),所以这个点是树的重心,每条路径的端点一定在两个不......
  • 个人题解:2024 年湖北省省队选拔集训暨能力测试 第一试
    时与风对每条边进行BFS。二维偏序部分用vector存一下就行了。花神诞日引理:对于\(a<b<c\),\(\min(a\text{XOR}b,b\text{XOR}c)\leqa\text{XOR}c\)。证明:考虑比较\(a,c\)二进制下第一位不同,也就是\(a=(X0\dots)_{(2)},c=(X1\dots)_{2}\)。因为\(b\in(a,c)\)所以......