1. 程式人生 > >NOIP 2012 洛谷P1081 開車旅行

NOIP 2012 洛谷P1081 開車旅行

span 旅行 城市 基礎上 esc strong using tro lap

Description:

就是兩個人開車,只能向東開。向東有n個城市,城市之間的距離為他們的高度差。A,B輪流開車,A喜歡到次近的城市,B喜歡到最近的城市。如果車子開到底了或者車子開的路程已經超過了限制X就停。

問你從一個點出發,最後A行駛的裏程數和B行駛的裏程數。

倍增的妙用,這道題改變了我對NOIP的看法。讓我對著書看了好久才看懂

不過70分還是好拿的,就是預處理然後對每個詢問$O(n)$ 模擬一遍。復雜度$O(nlog_{2}n+nm)$

怎麽預處理?就是找到一個城市$i$後離他最近的城市和次近的城市,分別為$gb[i]$,$ga[i]$,用平衡樹(set)或者鏈表或者權值線段樹實現

滿分在70分的基礎上,倍增預處理,然後$O(log_{2}n)$回答每個詢問

對於第一個詢問,枚舉起點即可。

接下來, $dp[i][j][k]$表示$k$在$j$點出發,共開$2^i$天的車到達的城市,1表示A,2表示B。

$sta[i][j][k]$表示$k$在$j$點出發,共開$2^i$天的車A所行駛的路程,$stb[i][j][k]$表示$k$在$j$點出發,共開$2^i$天的車B所行駛的路程。

邊界和初值:$dp[0][j][0]=ga[j]$,$dp[0][j][1]=g[j]$,$sta[0][j][0]=dist(j,ga[j])$,$stb[0][j][1]=dist(j,gb[j])$ 其他都為0

轉移看代碼,特別註意的是因為$i=1$時,兩人是換著開的,所以轉移的時候k要去個反。而$i>1$的話,$2^i$天和$2^{i-1}$時開車的人是相同的故不用取反

其他對著狀態就能理解了把,就不寫註釋了。

技術分享圖片
 1 /*代碼修改自李煜東霸霸,變量名是按照書上說法開的*/
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 const int N = 1e5 + 10;
 5 #define ll long long
 6 ll sta[22][N][2], stb[22][N][2], ansA, ansB, la, lb;
7 int dp[22][N][2], n, m, ga[N], gb[N], h[N], i, t, ans; 8 struct node{ int x, y;} ; 9 set<node> s; 10 set<node>::iterator it,lt,rt; 11 int dist(int x, int y){ return abs(h[x] - h[y]); } 12 bool cmp(int x, int y){ return dist(x, i) == dist(y, i) ? h[x] < h[y] : dist(x, i) < dist(y, i); } 13 bool operator < (node a, node b){ return a.y < b.y; } 14 void solve(int s, int X){ 15 la = lb = 0; int k = 0; 16 for(int j = t; j >= 0; j--) 17 if(dp[j][s][k] && sta[j][s][k] + stb[j][s][k] <= X){ 18 X -= (sta[j][s][k] + stb[j][s][k]); 19 la += sta[j][s][k], lb += stb[j][s][k]; 20 if(j == 0) k ^= 1; 21 s = dp[j][s][k]; 22 } 23 } 24 int main(){ 25 scanf("%d", &n); 26 for(int i = 1; i <= n; i++) scanf("%d", &h[i]); 27 for(i = n; i >= 1; i--){ 28 node tmp; tmp.x = i, tmp.y = h[i]; 29 int temp[10]; 30 s.insert(tmp); it = s.find(tmp); 31 lt = it, rt = it, m = 0; 32 if(lt != s.begin()) lt--, temp[++m] = lt -> x; 33 if(lt != s.begin()) lt--, temp[++m] = lt -> x; 34 if(rt++, rt != s.end()){ 35 temp[++m] = rt -> x; 36 if(rt++, rt != s.end()) temp[++m] = rt -> x; 37 } 38 sort(temp + 1, temp + m + 1, cmp); 39 if(m) gb[i] = temp[1]; 40 if(m > 1) ga[i] = temp[2]; 41 } 42 t = log(n * 1.0) / log(2.0); 43 for(i = 1; i <= n; i++){ 44 if(ga[i]) dp[0][i][0] = ga[i], sta[0][i][0] = dist(ga[i], i), stb[0][i][0] = 0; 45 if(gb[i]) dp[0][i][1] = gb[i], stb[0][i][1] = dist(gb[i], i), sta[0][i][1] = 0; 46 } 47 for(i = 1; i <= t; i++) 48 for(int j = 1; j <= n; j++) 49 for(int k = 0; k < 2; k++){ 50 int l; 51 if(i == 1) l = k ^ 1; else l = k; 52 if(dp[i - 1][j][k]) dp[i][j][k] = dp[i - 1][dp[i - 1][j][k]][l]; 53 if(dp[i][j][k]){ 54 sta[i][j][k] = sta[i - 1][j][k] + sta[i - 1][dp[i - 1][j][k]][l]; 55 stb[i][j][k] = stb[i - 1][j][k] + stb[i - 1][dp[i - 1][j][k]][l]; 56 } 57 } 58 int X0; 59 scanf("%d", &X0); ansA = 1, ansB = 0; 60 for(i = 1; i <= n; i++){ 61 solve(i, X0); 62 if(!lb) la = 1; 63 if(la * ansB < lb * ansA || (la * ansB == lb * ansA && h[i] > h[ans])) 64 ansA = la, ansB = lb, ans = i; 65 } 66 printf("%d\n", ans); 67 scanf("%d", &m); 68 int x, y; 69 while(m--){ 70 scanf("%d%d", &x, &y); 71 solve(x, y); 72 printf("%lld %lld\n", la, lb); 73 } 74 return 0; 75 }
View Code

NOIP 2012 洛谷P1081 開車旅行