洛谷P1081 开车旅行
考点:双向链表,倍增。
早上上数据结构课刚讲到链表,老师提了一句双向链表,我嘀咕了一句“没用”结果晚上刷题就见到了。
题解:这个题看似有选择但其实策略固定那么路径就已经固定了,所以我们不用每次都遍历计算下一站,预处理一下数据,将每个点的最近点与次近点找到记录下了,以后直接调用。
即使是预处理遍历做法的时间复杂度也仍是
0
(
n
2
)
0(n^2)
0(n2),我将数据按着海拔排一下序那么最近点与次近点一定在
i
+
1
,
i
−
1
,
i
+
2
,
i
−
2
i+1,i-1,i+2,i-2
i+1,i−1,i+2,i−2中。这里还需注意只能从西向东走(即从小下标向大下标),所以不能直接从左到右遍历,因为这样找到的最近点可能是在西边的。我们用p数组将排序前的下标与排序后的下标联系起来,计算时从最西边的城市开始,这样所有的其他城市都在他的东边,计算完之后将他删去,继续计算第二西的城市……如果是数组的话每次都删去的总时间复杂度是
0
(
n
2
)
0(n^2)
0(n2),所以要使用链表这样方便删去,但我们还需要调用一个数的前面的数,所以在链表的基础上加个前域就成了双向链表。
至此我们把预处理讲清楚了,接下来就是解决问题了,模拟a,b的暴力法可以得70分还是很不错的,最优方法是使用倍增,a,b各开一次算一步,我们可以记录所有点在2i步后的位置与a增加的路程与b增加的路程。我们每次跳着计算,这样原来一条路需要计算
n
n
n次,变成了一条路需要计算
l
o
g
n
logn
logn次,总时间复杂度也从
0
(
n
2
)
0(n^2)
0(n2)变成了
0
(
n
∗
l
o
g
n
)
0(n*logn)
0(n∗logn)。
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int MAX = 1e5 + 10;
int n, m;
ll x;
struct node {
int i, l, r;
ll h;
}city[MAX];
int s;
int p[MAX], near[MAX], cnear[MAX];
int f[MAX][21];
ll sta[MAX][21], stb[MAX][21];
bool cmp(node a, node b)
{
return a.h < b.h;
}
bool panduan(int i,int l,int r)
{
if (!l)return 0;
if (!r)return 1;
return (city[i].h - city[l].h) <= (city[r].h - city[i].h);
}
int pb(int j, int a, int b)
{
if (!a)return city[b].i;
if (!b)return city[a].i;
if ((city[j].h - city[a].h) <= (city[b].h - city[j].h))return city[a].i;
return city[b].i;
}
void make_st()
{
for (int j = 1; j <= 19; j++)
{
for (int i = 1; i <= n; i++)
{
f[i][j] = f[f[i][j - 1]][j - 1];
sta[i][j] = sta[i][j - 1] + sta[f[i][j - 1]][j - 1];
stb[i][j] = stb[i][j - 1] + stb[f[i][j - 1]][j - 1];
}
}
}
void getab(ll x, int p, int &a, int &b)
{
a = b = 0;
for (int j = 19; j >= 0; j--)
{
if (f[p][j] && (long long)(a + b + sta[p][j] + stb[p][j]) <= x)
{
a += sta[p][j];
b += stb[p][j];
p = f[p][j];
}
}
if (cnear[p] && a + b + sta[p][0] <= x)a += sta[p][0];
}
int main()
{
double minn = 1e9 + 10;
int ans = 0;
cin >> n;
for (int i = 1; i <= n; i++)cin >> city[i].h;
for (int i = 1; i <= n; i++)city[i].i = i;
sort(city + 1, city + 1 + n, cmp);
for (int i = 1; i <= n; i++)p[city[i].i] = i;
for (int i = 1; i <= n; i++) { city[i].l = i - 1; city[i].r = i + 1; }
city[1].l = city[n].r = 0;
for (int i = 1; i <= n; i++)
{
int j = p[i], l = city[j].l, r = city[j].r;
if (panduan(j, l, r)) { near[i] = city[l].i; cnear[i] = pb(j, city[l].l, r); }
else { near[i] = city[r].i; cnear[i] = pb(j, l, city[r].r); }
if (l)city[l].r = r;
if (r)city[r].l = l;
}
for (int i = 1; i <= n; i++)
{
f[i][0] = near[cnear[i]];
sta[i][0] = abs(city[p[i]].h - city[p[cnear[i]]].h);
stb[i][0] = abs(city[p[cnear[i]]].h - city[p[f[i][0]]].h);
}
make_st();
cin >> x >> m;
int a, b;
for (int i = 1; i <= n; i++)
{
getab(x, i, a, b);
if (b&&1.0*a / b < minn)
{
minn = 1.0*a / b;
ans = i;
}
}
cout << ans << endl;
for (int i = 1; i <= m; i++)
{
cin >> s >> x;
getab(x, s, a, b);
cout << a << ' ' << b << endl;
}
system("pause");
return 0;
}