codevs 1199 开车旅行
题意:
有n个城市每个城市都有一个海拔,城市与城市之间的距离为海拔的差的绝对值,现在有两个人A,B开车去旅行,首先A开车,然后B开车,依次交换,然后A会开向离他第二近的城市,B会开向离他最近的那个城市(规定距离相同时,海拔越低的城市距离越短),并且只能从左向右走,第一个问题问,他们总共行驶X个单位,问从哪个城市出发A开的距离比上B开的距离比值最小?第二个问题问,从城市S出发行驶X个单位后,A和B各自开了多少?
题解:
①从一个城市到达其他的城市只会是通过AB一轮的转移,这个转移是单调的,很显然倍增。
②现在的问题是解决倍增的初始值,就是处理出离城市i最近的城市和第二近的城市是谁,在下弱爆了,只能用set了QAQ
代码:
#include <iostream> #include <cstdio> #include <set> #include <map> #include <algorithm> using namespace std; #define ll long long const int N = 1e5 + 7; const ll inf = 5e9 + 7; map <ll, int> id; set <ll> S; ll va[20][N], vb[20][N], h[N];; ll cnta, cntb, x; struct node {ll h, c;} p[5]; int n, s, q; int anc[20][N], Anxt[N], Bnxt[N]; bool cmp (node a, node b) { if (a.c == b.c) return a.h < b.h; return a.c < b.c; } void query (int s, ll x, ll &cnta, ll &cntb) { for (int i = 18; i >= 0; --i) { if (anc[i][s] && va[i][s] + vb[i][s] <= x) { cnta += va[i][s]; cntb += vb[i][s]; x -= va[i][s] + vb[i][s]; s = anc[i][s]; } } if (Anxt[s] && abs(h[Anxt[s]] - h[s]) <= x) cnta += abs(h[Anxt[s]] - h[s]); } void prepare () { S.insert(-inf), S.insert (inf); for (int i = n; i >= 1; --i) { S.insert(h[i]); p[2].h = *--S.find(h[i]); p[3].h = *++S.find(h[i]); if (p[2].h != -inf) p[1].h = *--S.find(p[2].h); else p[1].h = -inf; if (p[3].h != inf) p[4].h = *++S.find(p[3].h); else p[4].h = inf; for (int k = 1; k <= 4; ++k) p[k].c = abs(p[k].h - h[i]); sort (p + 1, p + 5, cmp); Bnxt[i] = id[p[1].h]; Anxt[i] = id[p[2].h]; } for (int i = 1; i <= n; ++i) { int A = Anxt[i], B = Bnxt[A]; if (A) va[0][i] = abs(h[A] - h[i]); if (B) vb[0][i] = abs(h[B] - h[A]); anc[0][i] = B; } for (int i = 1; i <= 18; ++i) { for (int j = 1; j <= n; ++j) { anc[i][j] = anc[i-1][anc[i-1][j]]; va[i][j] = va[i-1][j] + va[i-1][anc[i-1][j]]; vb[i][j] = vb[i-1][j] + vb[i-1][anc[i-1][j]]; } } } int main () { scanf ("%d", &n); for (int i = 1; i <= n; ++i) { cin >> h[i]; id[h[i]] = i; } prepare(); cin >> x; double mini = 1e9 + 7; int id = 0; for (int i = 1; i <= n; ++i) { cnta = 0, cntb = 0; query (i, x, cnta, cntb); if (1.0 * cnta / cntb < mini) { mini = 1.0 * cnta / cntb; id = i; } } cout << id << endl; scanf ("%d", &q); while (q--) { cin >> s >> x; cnta = 0, cntb = 0; query (s, x, cnta, cntb); cout << cnta << " " << cntb << endl; } return 0; }
总结:
还是那句话,一开始尽量不要想细节,先想想总体的思路,要维护什么,要用什么算法就好了,不然总是把自己搞晕,这道题就是一个大水题,就是为了倍增去用set找边界。。。