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找边界。。。
浙公网安备 33010602011771号