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 }
View Code

 倍增优化DP就是一个划分状态+状态拼凑的过程

posted @ 2018-07-08 18:54  Ror_shach  阅读(385)  评论(0编辑  收藏  举报