Atcoder Grand Contest 019C Fountain Walk dp

Description


有一个1e8*1e8的网格图,保证每个格子是长度为100的正方形,在格点间行走时必须沿着水平或竖直方向,且只能在格点处拐弯
现在有n个直径为10的圆在不同格点上,保证同一行同一列最多只有一个圆。经过这些圆的时候必须绕着走
问从(x1,y1)走到(x2,y2)的最短路
n ≤ 2 ⋅ 1 0 5 n\le2\cdot10^5 n2105

Solution


很显然我们只会朝着两个方向走,并且会尽量在圆处拐弯
先扔掉肯定走不到的圆,根据行列最多1的性质可知选出来的圆一定是x、y坐标单调的,那么跑一个lis就可以了

一开始直接忽略了走一个半圆的情况,看到样例才反应过来。。可以发现我们最多只会走一次半圆,会走半圆当且仅当每一行或每一列都有圆

Code


#include 
#include 
#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

typedef std:: pair <int,int> pair;
const int INF=0x3f3f3f3f;
const double pi=acos(-1);
const int N=400005;

int f[N],s[N];
pair p[N],u[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	pair st,ed;
	st.fi=read(),st.se=read();
	ed.fi=read(),ed.se=read();
	int n=read(),tot=0;
	rep(i,1,n) p[i].fi=read(),p[i].se=read();
	if (st.fi>ed.fi) {st.fi*=-1,ed.fi*=-1; rep(i,1,n) p[i].fi*=-1;}
	if (st.se>ed.se) {st.se*=-1,ed.se*=-1; rep(i,1,n) p[i].se*=-1;}
	rep(i,1,n) if (p[i].fi>=st.fi&&p[i].fi<=ed.fi&&p[i].se>=st.se&&p[i].se<=ed.se) {
		u[++tot]=p[i];
	}
	std:: sort(u+1,u+tot+1);
	int mx=0;
	rep(i,1,tot) {
		f[i]=std:: lower_bound(s+1,s+mx+1,u[i].se)-s;
		if (f[i]>mx) s[mx=f[i]]=u[i].se;
		else s[f[i]]=std:: min(s[f[i]],u[i].se);
	}
	double ans=100.0*(ed.fi-st.fi+ed.se-st.se)-(20-5*pi)*mx;
	if (mx-1==std:: min(ed.fi-st.fi,ed.se-st.se)) ans+=5*pi;
	printf("%.12lf", ans);
	return 0;
}

你可能感兴趣的:(AtCoder,c++,一般dp,贪心)