NOIP2012 开车旅行 [提高组]

开车旅行


一道相当棒的倍增优化DP。

首先我们考虑这样一个问题:对于任意(a_i),求:

[min|a_i-a_j|(j>i) ]

若有多解,选择编号最小的。

对于这样的问题,我们可以利用平衡树(set)。对于(a_i)而言,它的最小值一定是它的前驱或后继。

我们不妨可以预处理每一位上最近的一位(g_b)即可,从(n)(1)倒序处理。

接下来是本题的重点:

定义:(f[i,j,0/1])代表从(j)这座城市要走(2^i)天,(0)代表小A先开车,(1)代表小B先开车所能够到达的位置。

(da[i,j,0/1])代表小A走的路程,(db[i,j,0/1])代表小B走的路程;


预处理后,我们开始解决问题了。对于一个问题,从出发点(j)开始,我们可以不断倍增,算出小A和小B各自所走路程。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
const int SIZE = 100000 + 5, INF = 2100000000;
struct city
{
	int id, h;
	bool operator < (const struct city &lhs) const
	{
		return h < lhs.h;
	}
};
multiset <city> p;
int n, m, t, disa, disb, x[SIZE], s[SIZE], f[40][SIZE][2];
int h[SIZE], da[40][SIZE][2], db[40][SIZE][2];
void calc(int cur, int dis)
{
	int op = 0;
	disa = disb = 0;
	for(int i = t; i >= 0; -- i)
	{
		if(f[i][cur][op] && disa + disb + da[i][cur][op] + db[i][cur][op] <= dis)
		{
			disa += da[i][cur][op], disb += db[i][cur][op];
			cur = f[i][cur][op];
			op = i == 0 ? 1 : 0;
		}
	}
	if(f[0][cur][op] && disa + disb + da[0][cur][op] + db[0][cur][op] <= dis) disa += da[0][cur][op], disb += db[0][cur][op];
	return;
}
int main()
{
	p.clear();
	scanf("%d", &n);
	t = log(n) / log(2);
	for(int i = 1; i <= n; ++ i) scanf("%d", &h[i]);
	scanf("%d", &x[0]);
	scanf("%d", &m);
	for(int i = 1; i <= m; ++ i) scanf("%d %d", &s[i], &x[i]);
	memset(f, 0, sizeof(f));
	memset(da, 0, sizeof(da));
	memset(db, 0, sizeof(db));

	city st;
	st.h = INF, st.id = 0;

	p.insert(st), p.insert(st);
	st.h = -INF, st.id = 0;
	p.insert(st), p.insert(st);

	int ga, gb, t1, t2;
	multiset <city> :: iterator itL, itR;
	city now;
	for(int j = n; j > 0; -- j)
	{
		now.h = h[j], now.id = j;
		p.insert(now);
		itR = p.lower_bound(now);
		itL = itR, -- itL, ++ itR;
		t1 = (*itL).h, t2 = (*itR).h;
		if(abs(h[j] - t1) <= abs(t2 - h[j])) gb = (*itL).id, -- itL;
		else gb = (*itR).id, ++ itR;
		t1 = (*itL).h, t2 = (*itR).h;
		if(abs(h[j] - t1) <= abs(t2 - h[j])) ga = (*itL).id;
		else ga = (*itR).id;
		f[0][j][0] = ga, f[0][j][1] = gb, da[0][j][0] = abs(h[j] - h[ga]), db[0][j][1] = abs(h[j] - h[gb]);
	}
	for(int i = 1; i <= t; ++ i)
	{
		for(int j = 1; j <= n; ++ j)
		{
			for(int op = 0; op < 2; ++ op)
			{
				if(i == 1)
				{
					f[i][j][op] = f[i - 1][f[i - 1][j][op]][1 - op];
					da[i][j][op] = da[i - 1][j][op] + da[i - 1][f[i - 1][j][op]][1 - op];
					db[i][j][op] = db[i - 1][j][op] + db[i - 1][f[i - 1][j][op]][1 - op];
				}
				else
				{
					f[i][j][op] = f[i - 1][f[i - 1][j][op]][op];
					da[i][j][op] = da[i - 1][j][op] + da[i - 1][f[i - 1][j][op]][op];
					db[i][j][op] = db[i - 1][j][op] + db[i - 1][f[i - 1][j][op]][op];
				}
			}
		}
	}
	double val = INF;
	int ans = h[n - 1] > h[n] ? n - 1 : n;
	for(int i = n - 2; i > 0; -- i)
	{
		calc(i, x[0]);
		if(val > (double)disa / disb)
		{
			val = (double)disa / disb;
			ans = i;
		}
		else if(val == (double)disa / disb && h[i] > h[ans]) ans = i;
	}
	printf("%d
", ans);
	for(int i = 1; i <= m; ++ i)
	{
		calc(s[i], x[i]);
		printf("%d %d
", disa, disb);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zach20040914/p/13817434.html