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找边界。。。

posted @ 2016-10-21 16:21  xgtao  阅读(198)  评论(0编辑  收藏  举报