NOIP 2012 洛谷P1081 开车旅行
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]=gb[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 }
倍增优化DP就是一个划分状态+状态拼凑的过程