【LOJ3248】「USACO 2020.1 Platinum」Falling Portals(凸包)(倍增)

传送门


题解:

把每个点的 S − T S-T ST 的图像画出来 f i ( x ) = − i x + A i f_i(x)=-ix+A_i fi(x)=ix+Ai。很明显要问的就是允许走交点的情况下 i i i 到达第 Q i Q_i Qi 的位置的最小横坐标是多少。

考虑 A [ Q i ] A[Q_i] A[Qi] A [ i ] A[i] A[i] 的大小关系,它们决定在 x = 0 x=0 x=0 的时候那条线在上面,于是我们的策略也就决定是向上跑还是向下跑。

不难发现具体的跑法就是贪心,就是能向上就向上,能向下就向下,当目标在上方的时候,始终走自己能达到的最大的纵坐标,否则始终走自己能达到的最小的纵坐标。

这样一来,每个点的决策是唯一的,预处理一下然后倍增即可。

预处理可以瞎写一个类似半平面交的东西,从某种意义上还更像是凸包。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}

char obuf[(int)(3e7+7)],*oh=obuf,ch[23];
template<typename T>void print(T a,char c){
	if(a<0)a=-a,*oh++='-';int tl=0;
	do ch[++tl]=a%10; while(a/=10);
	while(tl)*oh++=ch[tl--]^48;*oh++=c;
}struct obuf_flusher{~obuf_flusher(){fwrite(obuf,1,oh-obuf,stdout);}}Flusher;

}using namespace IO;

using std::cerr;
using std::cout;

cs int N=2e5+7,LOG=19;

cs double eps=1e-7;

int n,A[N],id[N];
inline bool cmp_id(int i,int j){
	return A[i]<A[j];
}inline double inter_x(int i,int j){
	return 1.*(A[i]-A[j])/(i-j);
}

int up[LOG][N],dn[LOG][N];

void build(){
	static int q[N],qn;q[qn=0]=0;
	for(int re i=n;i;--i){
		int p=id[i];
		while((qn&&inter_x(q[qn],p)<0)||(qn>1&&inter_x(p,q[qn])>inter_x(q[qn-1],q[qn])))--qn;
		dn[0][p]=q[qn];q[++qn]=p;
	}q[qn=0]=0;
	for(int re i=1;i<=n;++i){
		int p=id[i];
		while((qn&&inter_x(q[qn],p)<0)||(qn>1&&inter_x(p,q[qn])>inter_x(q[qn-1],q[qn])))--qn;
		up[0][p]=q[qn];q[++qn]=p;
	}
	for(int re i=1;i<LOG;++i)
		for(int re j=1;j<=n;++j){
			up[i][j]=up[i-1][up[i-1][j]];
			dn[i][j]=dn[i-1][dn[i-1][j]];
		}
}

bool go_up(int u,int v){
	if(!up[0][u])return false;
	double x=inter_x(up[0][u],u);
	double y=A[u]-u*x;
	return y+eps<=A[v]-x*v;
}

bool go_dn(int u,int v){
	if(!dn[0][u])return false;
	double x=inter_x(dn[0][u],u);
	double y=A[u]-u*x;
	return y-eps>=A[v]-x*v;
}

void Main(){n=gi();
	for(int re i=1;i<=n;++i)
		A[i]=gi(),id[i]=i;
	std::sort(id+1,id+n+1,cmp_id);build();
	for(int re i=1;i<=n;++i){
		int q=gi(),u=i;
		if(A[u]<A[q]){
			if(go_up(u,q)){
				for(int re i=LOG-1;~i;--i)
					if(go_up(up[i][u],q))u=up[i][u];
				u=up[0][u];
				if(u>q)print(-1,'\n');
				else {
					int x=A[q]-A[u],y=q-u;
					int g=std::__gcd(x,y);
					print(x/g,'/');print(y/g,'\n');
				}
			}else {
				if(u>q)print(-1,'\n');
				else {
					int x=A[q]-A[u],y=q-u;
					int g=std::__gcd(x,y);
					print(x/g,'/');print(y/g,'\n');
				}
			}
		}else {
			if(go_dn(u,q)){
				for(int re i=LOG-1;~i;--i)
					if(go_dn(dn[i][u],q))u=dn[i][u];
				u=dn[0][u];
				if(u<q)print(-1,'\n');
				else {
					int x=A[u]-A[q],y=u-q;
					int g=std::__gcd(x,y);
					print(x/g,'/');print(y/g,'\n');
				}
			}else {
				if(u<q)print(-1,'\n');
				else {
					int x=A[u]-A[q],y=u-q;
					int g=std::__gcd(x,y);
					print(x/g,'/');print(y/g,'\n');
				}
			}
		}
	}
}

inline void file(){
#ifdef zxyoi
	freopen("falling.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("falling.in","r",stdin);
	freopen("falling.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值