(AtCoder Grand Contest 019) C - Fountain Walk

题目链接

statememt

题目大意

一个城市,有很多横着和竖着的街道,相邻街道之间的距离为100。横着的街道最多有 108 条,标号为 0 ~ 1081 ,竖着的也是。(抽象成第一象限的方格平面, 0x,y1081 ,单位长度为100)
在某些十字路口(格点)处,有 n 个半径为10的喷泉,要经过喷泉时,只能从外围绕。给你起点 s 和终点 t 的坐标,问从 st 的最短距离是多少?(题目保证每一行、每一列最多只有一个喷泉,且起点和终点处没有喷泉,也没有重复的喷泉)

数据范围

0sx,sy,tx,ty<108
1n2000000xi,yi<108

解题思路

首先保证是曼哈顿距离,然后再来考虑喷泉的情况。
经过喷泉的方式分为两种:直穿和拐穿(顾名思义,直穿是绕喷泉走 180 ,拐穿是走 90
直穿喷泉要走 10π ,拐穿喷泉要走 5π ,相比于没有喷泉要走的距离20,很明显拐穿赚了!所以就要尽可能多地拐穿,尽可能少的拐穿。
st 的连线为对角线 肯定能形成一个矩形(线段在这儿就算特殊的矩形),要拐穿肯定拐穿矩形内的喷泉。将在矩形内的点按 x 排序,然后在 y 里找一个LIS,长度记为 lenlen 就是能最多能拐穿的喷泉数。

一种特殊情况例外:当 len==min(abs(txsx),abs(tysy))+1 时,必须要直穿一个喷泉!
举个例子:起点 s=(0,1) ,终点 t=(4,3),3 个喷泉,分别为 (1,1),(2,2),(3,3) 。拐穿记为1,直穿记为0。最短距离肯定是(0, 1) -> (1,1)0 -> (2,2)1 -> (3,3)1 -> (4, 3) 或者 (0, 1) -> (1,1)1 -> (2,2)1 -> (3,3)0 -> (4, 3),长度为 740+20π



外话

比赛时想到正解之后还有近两个小时的时间。那是就只考虑到 sx==sysy==ty 时才会直穿,当我意识到不单只有这种情况才会直穿时,已经over了。看了题解之后,思路相同,更是不快!
之后的补题,因为平时习惯以1开头,对题目中的 0 ~ 1081 不是很注意, 以致于求LIS时出了点小差错(代码48行)
然后WA成狗:look


详见代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 2e5;
//const double PI = 3.1415926535897932384626;
const double PI = acos(-1);

LL sx, sy, tx, ty;
int n, tot;
double res;
struct point
{
    LL x, y;
}tmp[MaxN + 5];

bool cmpx(point a, point b) {
    if(a.x == b.x) return a.y < b.y;
    else return a.x < b.x;
}

//----------------LIS---------------------
int len;
LL a[MaxN + 5], d[MaxN + 5];

int bin_search(LL x) {
    int l = 0, r = len;
    int mid = 0, ans = 0;
    while(l <= r) {
        mid = (l + r) >> 1;
        if(d[mid] >= x) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    return ans;
}

void get_LIS(bool c)
{
    len = 0;
    d[len] = -INF;
    //初始d[0] == 0的,如果LIS中有0,就会少算一个
    if(c == 1) {
        for(int i = 1; i <= tot; i++) 
            a[i] = tmp[i].y;
    }
    else {
        //求下降时将数组倒过来求最长
        for(int i = 1; i <= tot; i++)
            a[i] = tmp[tot - i + 1].y;
    }
    for(int i = 1; i <= tot; i++) {
        if(a[i] > d[len]) d[++len] = a[i];
        else {
            int pos = bin_search(a[i]);
            d[pos] = a[i];
        }
    }
}
//----------------------------------------

int main()
{
    tot = 0;
    res = 0.0;
    scanf("%lld %lld %lld %lld", &sx, &sy, &tx, &ty);
    if(sx > tx) {
        //让起点在左边
        swap(sx, tx); swap(sy, ty);
    }
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        LL x, y;
        scanf("%lld %lld", &x, &y);
        //将在 起点和终点所形成的矩形 中的点给抠出来
        if((min(sx, tx) <= x && x <= max(sx, tx)) && (min(sy, ty) <= y && y <= max(sy, ty)))
            tmp[++tot].x = x, tmp[tot].y = y;
    }
    sort(tmp + 1, tmp + tot + 1, cmpx);
    if(sy > ty) {
        //取最长下降子序列
        get_LIS(0);
    }
    else if(sy <= ty) {
        //取最长上升子序列
        get_LIS(1);
    }
    int num = min(abs(tx - sx), abs(ty - sy)) + 1;
    if(len == num) {
        //肯定会直线经过一个喷泉,其他都是拐角
        res = (100.0 * (abs(tx - sx) + abs(ty - sy)) - 20.0 * (LL)len) + (LL)(len - 1) * 5 * PI + 10 * PI;
    }
    else {
        //全是拐角经过
        res = (100.0 * (abs(tx - sx) + abs(ty - sy)) - 20.0 * (LL)len) + (LL)len * 5 * PI;
    }
    printf("%.15lf\n", res);
    return 0;
}

你可能感兴趣的:(AtCoder)