NOIP2012 開車旅行

2022-05-21 18:12:12 字數 3720 閱讀 3830

小 \(a\) 和小 \(b\) 決定利用假期外出旅行,他們將想去的城市從 \(1\) 到 \(n\) 編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 \(i\)的海拔高度為 \(h_i\),城市 \(i\) 和城市 \(j\) 之間的距離 \(d_\) ,\([i,j]\)恰好是這兩個城市海拔高度之差的絕對值,即 \(d_=|h_i-h_j|\)。

旅行過程中,小 \(a\) 和小 \(b\) 輪流開車,第一天小 \(a\) 開車,之後每天輪換一次。他們計畫選擇乙個城市 \(s\) 作為起點,一直向東行駛,並且最多行駛 \(x\) 公里就結束旅行。小 \(a\) 和小 \(b\) 的駕駛風格不同,小 \(b\) 總是沿著前進方向選擇乙個最近的城市作為目的地,而小\(a\)總是沿著前進方向選擇第二近的城市作為目的地(注意:本題中如果當前城市到兩個城市的距離相同,則認為離海拔低的那個城市更近)。如果其中任何一人無法按照自己的原則選擇目的城市,或者到達目的地會使行駛的總距離超出 \(x\) 公里,他們就會結束旅行。

在啟程之前,小 \(a\) 想知道兩個問題:

對於乙個給定的 \(x=x_0\),從哪乙個城市出發,小 \(a\) 開車行駛的路程總數與小 \(b\) 行駛的路程總數的比值最小(如果小 \(b\) 的行駛路程為 \(0\) ,此時的比值可視為無窮大,且兩個無窮大視為相等)。如果從多個城市出發,小 \(a\) 開車行駛的路程總數與小 \(b\) 行駛的路程總數的比值都最小,則輸出海拔最高的那個城市。

對任意給定的 \(x=x_i\) 和出發城市 \(s_i\) ,小 \(a\) 開車行駛的路程總數以及小 \(b\) 行駛的路程總數。

第一行包含乙個整數 \(n\) ,表示城市的數目。

第二行有 \(n\) 個整數,每兩個整數之間用乙個空格隔開,依次表示城市 \(1\) 到城市 \(n\) 的海拔高度,即 \(h_1\),\(h_2\),\(…\),\(h_n\) ,且每個 \(h_i\) 都是不同的。

第三行包含乙個整數 \(x_0\)。

第四行為乙個整數 \(m\) ,表示給定 \(m\) 組 \(s_i\)和 \(x_i\) 。

接下來的 \(m\) 行,每行包含 \(2\) 個整數 \(s_i\) 和 \(x_i\), 表示從城市 \(s_i\) 出發,最多行駛 \(x_i\) 公里。

輸出共 \(m+1\) 行。

第一行包含乙個整數 \(s_0\),表示對於給定的 \(x_0\),從編號為 \(s_0\) 的城市出發,小 \(a\) 開車行駛的路程總數與小 \(b\) 行駛的路程總數的比值最小。

接下來的 \(m\) 行,每行包含 \(2\) 個整數,之間用乙個空格隔開,依次表示在給定的 \(s_i\) 和 \(x_i\) 下小 \(a\) 行駛的里程總數和小 \(b\) 行駛的里程總數。

對於30%的資料,有 \(1≤n≤20\),\(1≤m≤20\) ;

對於40%的資料,有 \(1≤n≤100\),\(1≤m≤100\);

對於50%的資料,有 \(1≤n≤100\),\(1≤m≤1,000\);

對於70%的資料,有 \(1≤n≤1,000\),\(1≤m≤10,000\) ;

對於100%的資料,有 \(1≤n≤100,000\),\(1≤m≤100,000\), \(-10^9≤h_i≤10^9\) ,資料保證 \(h_i\) 互不相同。

我們先來看前70%的資料,我們發現可以用 \(o(n^2)\) 的預處理來算出小\(a\)在某個位置i的下乙個位置和小\(b\)在某個位置的下乙個位置。然後對於第乙個問題我們可以列舉每乙個出發點,然後進行比較,對於第二問我們可以直接模擬計算即可。

我們再來考慮100%的演算法,我們可以發現上面這個演算法最最耗時的地方乙個是在預處理上,乙個是在列舉和模擬上。

我們依次來考慮是否可以優化:

1.對於 \(o(n^2)\) 的預處理時,我們可以利用資料結構來優化使其降至 \(o(nlogn)\) ,我們需要乙個支援插入操作和求區間第k大的資料結構,通常我們會用線段樹、雙向鏈、set、平衡樹來維護,蒟蒻我對於其他幾種不是很了解,只能用set來水了。注意在用set時迭代器是否越界。

2.對於列舉演算法,我麼發現我們找不出什麼好的優化演算法,所以我們只能放棄。

3.對於模擬,我們發現,是小\(a\)和小\(b\)輪流進行,我們可以考慮用倍增進行優化就像樹上倍增求lca一樣,我們把小\(a\)和小\(b\)各走一步看作是一輪,記錄\(f[i][j]\)表示從\(i\)出發經過\(2^j\)輪後他們回到達的地方,記錄\(fa[i][j]\)表示從\(i\)出發,經過\(2^j\)輪後小\(a\)走過的路程,同理記錄小\(b\)的為\(fb[i][j]\)。

由於\(2^ + 2^ = 2^j\) ,考慮一下狀態的轉移:

\(f[i][j] = f[f[i][j-1]][j-1]\);

\(fa[i][j] = fa[i][j-1] + fa[f[i][j-1]][j-1]\);

\(fb[i][j] = fb[i][j-1] + fb[f[i][j-1]][j-1]\);

值得注意的是:

1.要考慮初始條件;

2.在進行倍增的過程中,如果無法兩個人都走的時候注意考慮小\(a\)是否可以單獨多走一步;

3.在遞推過程中,我們考慮一下狀態的產生順序,發現要以j所在的維度為階段。

#includeusing namespace std;

int n, m, s, x;

long long h[100005];

int a[100005], b[100005];

struct node

};inline node make_node(int x, int y)

set set;

node dis[5];

inline bool comp(const node & x, const node & y)

void init1()

++ it;

if(it != set.end())

for(int j = 1; j <= cnt; ++ j) dis[j].val -= h[i];

sort(dis + 1, dis + cnt + 1, comp);

a[i] = dis[2].pos, b[i] = dis[1].pos;

}return;

}int f[100005][20];

long long fa[100005][20], fb[100005][20];

int t;

void init2()

for(int j = 1; j <= t; ++ j)

for(int i = 1; i <= n; ++ i)

f[i][j] = f[f[i][j - 1]][j - 1], fa[i][j] = fa[i][j - 1] + fa[f[i][j - 1]][j - 1], fb[i][j] = fb[i][j - 1] + fb[f[i][j - 1]][j - 1];

}void get_ans(int s, int x0, long long & suma, long long &sumb)

int posa = a[s];

if(!posa) return;

if(abs(h[posa] - h[s]) <= x0) suma += abs(h[posa] - h[s]);

return;

}int main()

printf("%d\n", s);

scanf("%d", &m);

for(int i = 1; i <= m; ++ i)

return 0;

}

NOIp2012 開車旅行

傳送門 以後序列上的問題可以想一想倍增。s a i s b i sa i sb i sa i sb i 記錄在i ii這個位置讓a b a ba b開車到達的點。把a aa和b bb都跳一次稱為一輪。d is i j dis i j dis i j 表示從i ii跳2 j2 j 2j輪的總路程。p ...

Noip2012 開車旅行

小 a 和小 b 決定利用假期外出旅行,他們將想去的城市從 1 到 n 編號,且編號較小的城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為hi,城市 i 和城市 j 之間的距離 d i,j 恰好是這兩個城市海拔高度之差的絕對值,即d i,j hi hj 旅行過程中...

NOIP2012 開車旅行

題目描述 小 a 和小 b 決定利用假期外出旅行,他們將想去的城市從 1 到 n 編號,且編號較小的 城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為 hi,城市 i 和城市 j 之間的距離 d i,j 恰好是這兩個城市海拔高度之差的絕對值,即 d i,j hi ...