-
- 139通過
- 484提交
- 題目提供者洛谷OnlineJudge
- 標簽倍增2012NOIp提高組
- 難度提高+/省選-
題目描述
小 A 和小 B 決定利用假期外出旅行,他們將想去的城市從 1 到 N 編號,且編號較小的
城市在編號較大的城市的西邊,已知各個城市的海拔高度互不相同,記城市 i 的海拔高度為
Hi,城市 i 和城市 j 之間的距離 d[i,j]恰好是這兩個城市海拔高度之差的絕對值,即
d[i,j] = |Hi− Hj|。
旅行過程中,小 A 和小 B 輪流開車,第一天小 A 開車,之后每天輪換一次。他們計划
選擇一個城市 S 作為起點,一直向東行駛,並且最多行駛 X 公里就結束旅行。小 A 和小 B
的駕駛風格不同,小 B 總是沿着前進方向選擇一個最近的城市作為目的地,而小 A 總是沿
着前進方向選擇第二近的城市作為目的地(注意:本題中如果當前城市到兩個城市的距離
相同,則認為離海拔低的那個城市更近)。如果其中任何一人無法按照自己的原則選擇目的
城市,或者到達目的地會使行駛的總距離超出 X 公里,他們就會結束旅行。
在啟程之前,小 A 想知道兩個問題:
1.對於一個給定的 X=X0,從哪一個城市出發,小 A 開車行駛的路程總數與小 B 行駛
的路程總數的比值最小(如果小 B 的行駛路程為 0,此時的比值可視為無窮大,且兩個無窮大視為相等)。如果從多個城市出發,小 A 開車行駛的路程總數與小 B 行駛的路程總數的比
值都最小,則輸出海拔最高的那個城市。
- 對任意給定的 X=Xi和出發城市 Si,小 A 開車行駛的路程總數以及小 B 行駛的路程
總數。
輸入輸出格式
輸入格式:
第一行包含一個整數 N,表示城市的數目。
第二行有 N 個整數,每兩個整數之間用一個空格隔開,依次表示城市 1 到城市 N 的海
拔高度,即 H1,H2,……,Hn,且每個 Hi都是不同的。
第三行包含一個整數 X0。
第四行為一個整數 M,表示給定 M 組 Si和 Xi。
接下來的 M 行,每行包含 2 個整數 Si和 Xi,表示從城市 Si出發,最多行駛 Xi公里。
輸出格式:
輸出共 M+1 行。
第一行包含一個整數 S0,表示對於給定的 X0,從編號為 S0的城市出發,小 A 開車行駛
的路程總數與小 B 行駛的路程總數的比值最小。
接下來的 M 行,每行包含 2 個整數,之間用一個空格隔開,依次表示在給定的 Si和
Xi下小 A 行駛的里程總數和小 B 行駛的里程總數。
輸入輸出樣例
drive1 4 2 3 1 4 3 4 1 3 2 3 3 3 4 3 drive2 10 4 5 6 1 2 3 7 8 9 10 7 10 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 7 10 7
drive1 1 1 1 2 0 0 0 0 0 drive2 2 3 2 2 4 2 1 2 4 5 1 5 1 2 1 2 0 0 0 0 0
說明
【輸入輸出樣例 1 說明】
各個城市的海拔高度以及兩個城市間的距離如上圖所示。
如果從城市 1 出發,可以到達的城市為 2,3,4,這幾個城市與城市 1 的距離分別為 1,1,2,
但是由於城市 3 的海拔高度低於城市 2,所以我們認為城市 3 離城市 1 最近,城市 2 離城市
1 第二近,所以小 A 會走到城市 2。到達城市 2 后,前面可以到達的城市為 3,4,這兩個城
市與城市 2 的距離分別為 2,1,所以城市 4 離城市 2 最近,因此小 B 會走到城市 4。到達城
市 4 后,前面已沒有可到達的城市,所以旅行結束。
如果從城市 2 出發,可以到達的城市為 3,4,這兩個城市與城市 2 的距離分別為 2,1,由
於城市 3 離城市 2 第二近,所以小 A 會走到城市 3。到達城市 3 后,前面尚未旅行的城市為
4,所以城市 4 離城市 3 最近,但是如果要到達城市 4,則總路程為 2+3=5>3,所以小 B 會
直接在城市 3 結束旅行。
如果從城市 3 出發,可以到達的城市為 4,由於沒有離城市 3 第二近的城市,因此旅行
還未開始就結束了。
如果從城市 4 出發,沒有可以到達的城市,因此旅行還未開始就結束了。
【輸入輸出樣例 2 說明】
當 X=7 時,
如果從城市 1 出發,則路線為 1 -> 2 -> 3 -> 8 -> 9,小 A 走的距離為 1+2=3,小 B 走的
距離為 1+1=2。(在城市 1 時,距離小 A 最近的城市是 2 和 6,但是城市 2 的海拔更高,視
為與城市 1 第二近的城市,所以小 A 最終選擇城市 2;走到 9 后,小 A 只有城市 10 可以走,
沒有第 2 選擇可以選,所以沒法做出選擇,結束旅行)
如果從城市 2 出發,則路線為 2 -> 6 -> 7 ,小 A 和小 B 走的距離分別為 2,4。
如果從城市 3 出發,則路線為 3 -> 8 -> 9,小 A 和小 B 走的距離分別為 2,1。
如果從城市 4 出發,則路線為 4 -> 6 -> 7,小 A 和小 B 走的距離分別為 2,4。
如果從城市 5 出發,則路線為 5 -> 7 -> 8 ,小 A 和小 B 走的距離分別為 5,1。
如果從城市 6 出發,則路線為 6 -> 8 -> 9,小 A 和小 B 走的距離分別為 5,1。
如果從城市 7 出發,則路線為 7 -> 9 -> 10,小 A 和小 B 走的距離分別為 2,1。
如果從城市 8 出發,則路線為 8 -> 10,小 A 和小 B 走的距離分別為 2,0。
全國信息學奧林匹克聯賽(NOIP2012)復賽
提高組 day1
第 7 頁 共 7 頁
如果從城市 9 出發,則路線為 9,小 A 和小 B 走的距離分別為 0,0(旅行一開始就結
束了)。
如果從城市 10 出發,則路線為 10,小 A 和小 B 走的距離分別為 0,0。
從城市 2 或者城市 4 出發小 A 行駛的路程總數與小 B 行駛的路程總數的比值都最小,
但是城市 2 的海拔更高,所以輸出第一行為 2。
【數據范圍】
對於 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≤10,000,-1,000,000,000≤Hi≤1,000,000,000,
0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,數據保證 Hi互不相同。
NOIP 2012 提高組 第一天 第三題
分析:這道題不會做,看別人題解看了一下午,算是有一點心得吧,分享一下,首先看數據,似乎可以暴力個部分分,每次選取一個點就開始模擬行走.每次都要尋找第一近和第二近,非常浪費時間,那么就預處理,怎么預處理呢?排序嗎?有點復雜,離得最近的一定是高度差相近的,那么能排序並且在相近的元素中尋找的數據結構是什么呢?STL的set!每次找兩個前驅和后繼,然后排序取前兩個.不過要注意,插入set要倒着插,因為每次都是去東邊的城市.但是這樣預處理之后還是不能通過所有的數據,我們來對算法仔細的分析,查找第一近和第二近的優化過了,枚舉點似乎不能優化,那么只能優化模擬行走了.會發現兩次行走可以合並成一次,有沒有什么更快的枚舉技巧呢?二分?似乎不行.那么就是倍增了.設f[i][j]為在位置i走2^j輪的位置,一輪就是A走和B走一次,fa[i][j]就是A在位置i走2^j輪的距離,fb[i][j]類同,那么怎么算呢?記住一個重要的性質:a^i-1 + a^i-1 = a^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類同,因為n<=100000,所以2^i,i從19開始枚舉,從大到小枚舉,依次判斷行不行即可.(真是復雜......)
對枚舉一個常見的優化:倍增.
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <set> using namespace std; const int maxn = 100005; int n, x0, m, k,x1, na[maxn], nb[maxn],ans; long long ansa = 1e5,ansb = 0ll,fa[maxn][20], fb[maxn][20], f[maxn][20]; struct node { int id, high; bool operator < (const node & b) const { return high < b.high; } }c[maxn]; struct node2 { int id, gaoducha; bool operator < (const node2 & b) const { if (gaoducha != b.gaoducha) return gaoducha < b.gaoducha; else return c[id].high < c[b.id].high; } }temp[5]; set <node> s; void init(int i) { set <node> ::iterator it = s.find(c[i]); int j = 0; if (it != s.begin()) { --it; temp[++j] = (node2) { it->id, abs(it->high - c[i].high) }; if (it != s.begin()) { --it; temp[++j] = (node2) { it->id, abs(it->high - c[i].high) }; ++it; } ++it; } if ((++it) != s.end())//s.end()是結尾之后的! { temp[++j] = (node2) { it->id, abs(it->high - c[i].high) }; if ((++it) != s.end()) temp[++j] = (node2) { it->id, abs(it->high - c[i].high) }; } sort(temp + 1, temp + j + 1); nb[i] = temp[1].id; if (j == 1) return; na[i] = temp[2].id; return; } void query(int st, int x, long long &ta, long long &tb) { for (int i = 19; i >= 0; i--) if (f[st][i] && fa[st][i] + fb[st][i] <= x) { ta += fa[st][i]; tb += fb[st][i]; x -= fa[st][i] + fb[st][i]; st = f[st][i]; } if (!na[st]) return; int tempans = abs(c[na[st]].high - c[st].high); if (tempans <= x) ta += tempans; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &c[i].high); c[i].id = i; } for (int i = n; i;i--) { s.insert(c[i]); if (i != n) init(i); } for (int i = 1; i <= n; i++) { int p1 = na[i], p2 = nb[na[i]]; fa[i][0] = p1 ? abs(c[p1].high - c[i].high) : 0; fb[i][0] = p2 ? abs(c[p2].high - c[p1].high) : 0; f[i][0] = p2; } for (int j = 1; j < 20;j++) //后更新的是j所以先枚舉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]; } scanf("%d", &x0); ans = 0; for (int i = 1; i <= n; i++) { long long ta = 0ll, tb = 0ll; query(i, x0, ta, tb); if (tb && (!ans || ansa * tb > ansb * ta)) { ansa = ta; ansb = tb; ans = i; } } printf("%d\n", ans); scanf("%d", &m); while (m--) { scanf("%d%d", &k, &x1); long long ta = 0ll, tb = 0ll; query(k, x1, ta, tb); printf("%d %d\n", ta, tb); } return 0; }
orz神犇raffica