(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 <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章