zoukankan      html  css  js  c++  java
  • 题解-Magic Ship

    Magic Ship

    你在 ((x_1,y_1)),要到点 ((x_2,y_2))。风向周期为 (n),一个字符串 (s{n}) 表示风向(每轮上下左右),每轮你都会被风向吹走一格。你可以在被风吹得被动移动的基础上选择每轮移动一格或不移动。求最少几轮可以到达终点。

    数据范围:(0le x_1,y_1,x_2,y_2le 10^9)(1le nle 10^5)


    一句话题解:在最优行进策略中,人离终点的距离与风周期数之间的函数单调递减;二分答案轮数。


    蒟蒻的前置准备:

    先通过坐标系平移简化计算:将起点 (start) 置为原点,则终点 (end)((x_2-x_1, y_2-y_1))

    风向字符 (s_i) 对应的四个字符 ('U')('D')('L')('R') 分别表示上下左右走一格,对应位移向量 ( extrm{w}) 为:

    ( extrm{w}('U')=(0,1))( extrm{w}('D')=(0,-1))( extrm{w}('L')=(-1,0))( extrm{w}('R')=(1,0)),字符 (s_i) 对应的位移向量 (vec{m_i}= extrm{w}(s_i))

    令位移前缀和为:(vec{ad_i}=sum_{j=1}^i vec{m_i}),则一个风周期的总位移为 (vec{ad_n})

    上式表明:尽管在一个长度为 (n) 轮的风周期内,人被移动的轨迹是复杂的,但每个风周期对人的轨迹影响是恒定的。因此按风周期来看,人的移动与风周期之间的关系是线性的。


    设当前走了 (k) 轮:

    假如不考虑人主动的走动,只考虑风吹人动,那么人的位置为 (left(ad_{kmod n}+ad_{n}cdot lfloorfrac kn floor ight))

    lowd1.jpg

    这时再考虑人的主动走动,在 (k) 轮中人最多走 (k)

    ( extrm{dis}(a,b)) 表示 (a)(b) 两点间的曼哈顿距离(x) 轴距离与 (y) 轴距离之和)。

    所以如果 ( extrm{dis}(end,now)le k),那么 (k) 轮以内即可到达终点。


    找到单调性:

    对于式子 (left(ad_{kmod n}+ad_{n}cdot lfloorfrac kn floor ight))

    如果枚举 (i=kmod n),则点 (now) 的移动轨迹可以是一条直线。

    lowd2.jpg图炸了回复反馈

    这时单独考虑 ( extrm{dis}(end,now))

    lowd3.jpg图炸了回复反馈

    可以发现 (lfloorfrac kn floorleftrightarrow extrm{dis}(end,now)) 的函数图像如下:(如果随风就能飘到终点,折线能与 (lfloorfrac kn floor) 轴相切)

    lowd4.jpg图炸了回复反馈

    因为最终要比较 ( extrm{dis}(end,now))(k),所以作函数 (lfloorfrac kn floorleftrightarrow extrm{dis}(end,now)-k)

    lowd5.jpg图炸了回复反馈

    这时有个问题:这折线是否单调递减?蒟蒻拿图说服你你肯定不信,但是可以这么想:

    目的是距离终点更近,风力和人力相等,所以就算风再捣乱,如果人走得和风对着干,与终点的距离也不会变大。

    所以这个折线是单调递减的。

    可是其实还有一种情况——该折线有平行于 (lfloorfrac kn floor) 轴的一段且与 (lfloorfrac kn floor) 轴没有交点,便说明对于当前的 (i=kmod n) 无解。


    上文提到:

    如果 ( extrm{dis}(end,now)le k),那么 (k) 轮以内即可到达终点。

    所以可以先如上文枚举 (i=kmod n),然后二分 (lfloorfrac kn floor),得到 (lfloorfrac kn floorleftrightarrow extrm{dis}(end,now)-k) 图像上 (le 0) 的临界点整数 (lfloorfrac kn floor)(k) 就是对于这个 (i) 的答案。总答案是对于每个 (i) 的答案的最小值,如果对于每个 (i) 都无解,输出 (-1)

    如上图,(lfloorfrac kn floor=2),可以通过 (i) 推算出 (k)


    好了,小蒟蒻成功写出了一篇没人看得懂的题解。还是放代码吧(其实很短):

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    #define lng long long
    #define db double
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define rz resize
    #define sz(x) (int)((x).size())
    const int inf=0x3f3f3f3f;
    const lng INF=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=1e5;
    int n; char s[N+7];
    lng ans=INF;
    
    //Point
    typedef pair<lng,lng> point;
    point st,ed,ad[N+7];
    lng dis(point x,point y){return abs(x.fi-y.fi)+abs(x.se-y.se);}
    point operator-(const point x,const point y){return mk(x.fi-y.fi,x.se-y.se);}
    point operator+(const point x,const point y){return mk(x.fi+y.fi,x.se+y.se);}
    point operator*(const point x,const lng y){return mk(x.fi*y,x.se*y);}
    
    //Main
    int main(){
    	scanf("%lld%lld%lld%lld%d%s",&st.fi,&st.se,&ed.fi,&ed.se,&n,&s[1]);
    	ed=ed-st;
    	if(ed.fi==0&&ed.se==0) return puts("0"),0;
    	for(int i=1;i<=n;i++)
    		if(s[i]=='U') ad[i]=ad[i-1]+mk(0,1);
    		else if(s[i]=='D') ad[i]=ad[i-1]+mk(0,-1);
    		else if(s[i]=='L') ad[i]=ad[i-1]+mk(-1,0);
    		else if(s[i]=='R') ad[i]=ad[i-1]+mk(1,0);
    	for(int i=1;i<=n;i++){
    		lng l=-1,r=1e12+1;
    		while(l<r-1){
    			lng mid((l+r)>>1);
    			if(dis(ad[i]+ad[n]*(mid),ed)-mid*n-i<=0) r=mid;
    			else l=mid;
    		}
    		if(dis(ad[i]+ad[n]*r,ed)-r*n-i<=0) ans=min(ans,r*n+i);
    	}
    	if(ans==INF) puts("-1");
    	else printf("%lld
    ",ans);
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    使用getopts处理Shell脚本参数
    SAP R/3 MM模块学习笔记
    应 阿成1 要求 co主要业务操作手册
    AIX磁盘管理命令
    工作中心和工艺路线
    顾问学院培训教材 TAMM ,TAPP,TASD,TACO
    十个“三角形”汉字,好看、不好认
    ALV做出的报表里更改布局里没有保存按钮的解决方法
    SAP系统内的发票校验
    修改SAP 登录后的背景图片
  • 原文地址:https://www.cnblogs.com/Wendigo/p/12819305.html
Copyright © 2011-2022 走看看