本题困扰了我一天,所以我要写个总结纪念纪念。
首先,这题的难点再于如何将这又臭又长的题面转换一下。我用了40min左右的时间认真思考,科学探究,最后发现这就是个树上倍增。很容易发现每个城市如果确定了是A或B开车的话,那么,他们一路向东的路径是确定的,如果我们建立一个虚点,将汽车停掉的那个点连向虚点,毫无疑问,就出来了一棵树,每个点只有一个父亲(除了虚点),然后我们就相当于问一个点在那条树链上走,最多走到哪,并把路径长度交错记下来。于是我们就将一个点拆成两个,分别代表A往上连边的点和B往上连边的点。找到每个点的父亲,构出整棵树。(将本题转换为树是解题的关键,不过应该也有其他基于链的作法,直接倍增)
虽然我yy出了这样一个麻烦的作法,代码写了250多行,调了n个小时,但是我还是要继续BB下去,将基于树上倍增的作法讲完。
很明显,我们求在一条树链上能走多远,如果一步一步枚举就太慢了,于是构出树以及边权后直接倍增。我们记两个数组, dep[i] 表示走到虚点(就是停车)位置A和B开车的总路程,这个是拿来与限制距离 x 比较的,另外,由于要知道A和B各自开了多远(两个老司机,交替驾驶不疲劳),于是我们多开一个数组 dA[] 记录A的开车路程。然后树上倍增,求出来,后面的询问都迎刃而解了(两个子任务是一样的,如果能求出每次A,B行驶距离的话)。
这里的时间是 O(mlogn) 的。
现在的问题在于如何快速求一个点在树中的父亲,就是某个城市的下一个城市是哪里?蒟蒻我思维江化,一心只想着nlogn数据结构,于是就写了个线段树+离散化,成功让我的代码变得长长长。我们将城市按海拔排序后,每个城市离散海拔,然后每次就在线段树中找第一个大于当前海拔的城市,第一个小于的,第二个大于的,和第二个小于的,然后搞一搞。注意这里有很多细节,我的程序一开始在这里错漏百出,将坐标和排名混淆的不像话(fw反向映射大法好),最后还好调出来了,诸多细节就请见代码了(rank还是改成Rank比较安全,可能CE)。我们用线段树维护最左边的1,就将所有的清为oo,取最小值就行了,维护最右边的1也一样,主要是二叉查找没有这个好写。从后往前做一遍,不断插入,就可以 O(nlogn) 建出图。
然而这里可以不必如此麻烦,直接用双向链表记一下(i-1,i-2,i+1,i+2),一边做,一边动态修改链表,从左往右的话就删除元素,然后也可以很快求出来,比线段树少了个log。我写线段树就权当练习吧。我的链表学得太差了,整天想着一维数据结构里的线段树和平衡树,链表,堆,栈,队列这些基础数据结构不能忘啊,NOIP经常就考,一考就送。
这题就这样切掉了,用了我挺多时间,证明我的调试能力有待提高,但还是要循序渐进。写代码很快,调试很久又有什么用。Think twice,code once. 这句话再怎么强调都不为过。就这样吧。
#include
#include
#include
#include
#include
#include
#define maxn 100100
#define oo 0x7fffffff
#define Eps 1e-10
using namespace std;
typedef long long LL;
int n, m, s, ansx;
int head_p[maxn<<1], cur = -1, Rank[maxn];
int que[maxn<<1], head, tail, fw[maxn];
LL dep[maxn<<1], dA[maxn<<1], x0, xx;
int f[20][maxn<<1];
double rate = oo;
struct Adj{
int next, obj;
LL len;
}Edg[maxn<<1];
struct City{
LL height;
int id;
}cc[maxn];
struct Tnode{
int Minid, Maxid;
}tree[maxn<<2];
bool cmp1(City A, City B){
return A.height < B.height;
}
bool cmp2(City A, City B){
return A.id < B.id;
}
void build(int root, int L, int R){
if(L == R){
tree[root].Minid = tree[root].Maxid = -1;
return;
}
int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
build(Lson, L, mid);
build(Rson, mid+1, R);
tree[root].Minid = tree[root].Maxid = -1;
}
void update(int root, int L, int R, int x, int id){
if(x > R || x < L) return;
if(L == x && x == R){
tree[root].Minid = tree[root].Maxid = id;
return;
}
int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
update(Lson, L, mid, x, id);
update(Rson, mid+1, R, x, id);
if(tree[Lson].Minid == -1) tree[root].Minid = tree[Rson].Minid;
else if(tree[Rson].Minid == -1) tree[root].Minid = tree[Lson].Minid;
else tree[root].Minid = min(tree[Lson].Minid, tree[Rson].Minid);
if(tree[Lson].Maxid == -1) tree[root].Maxid = tree[Rson].Maxid;
else if(tree[Rson].Maxid == -1) tree[root].Maxid = tree[Lson].Maxid;
else tree[root].Maxid = max(tree[Lson].Maxid, tree[Rson].Maxid);
}
int query(int root, int L, int R, int x, int y, int sign){
if(x > R || y < L) return -1;
if(x <= L && y >= R){
if(!sign) return tree[root].Minid;
else return tree[root].Maxid;
}
int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
int temp1 = query(Lson, L, mid, x, y, sign);
int temp2 = query(Rson, mid+1, R, x, y, sign);
if(temp1 == -1) return temp2;
else if(temp2 == -1) return temp1;
else{
if(!sign) return min(temp1, temp2);
else return max(temp1, temp2);
}
}
void Insert(int a, int b, LL c){
cur ++;
Edg[cur].next = head_p[a];
Edg[cur].obj = b;
Edg[cur].len = c;
head_p[a] = cur;
}
void Da(){
memset(dep, -1, sizeof(dep));
dep[0] = 0;
dA[0] = 0;
f[0][0] = 0;
que[0] = 0;
while(head <= tail){
int now = que[head++];
for(int i = head_p[now]; ~ i; i = Edg[i].next){
int v = Edg[i].obj;
LL l = Edg[i].len;
if(~ dep[v]) continue;
dep[v] = dep[now] + l;
dA[v] = dA[now];
if(v > n) dA[v] += l;
f[0][v] = now;
que[++tail] = v;
}
}
for(int i = 1; i <= 19; i++)
for(int j = 0; j <= (n<<1); j++)
f[i][j] = f[i-1][f[i-1][j]];
}
LL get_d(int x, int y){
return abs(cc[x].height - cc[y].height);
}
int main(){
freopen("lgP1081.in", "r", stdin);
freopen("lgP1081.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lld", &cc[i].height);
cc[i].id = i;
}
for(int i = 0; i <= (n<<1); i++) head_p[i] = -1;
sort(cc+1, cc+n+1, cmp1);
Rank[cc[1].id] = 1;
for(int i = 2; i <= n; i++)
Rank[cc[i].id] = Rank[cc[i-1].id] + 1;
for(int i = 1; i <= n; i++) fw[Rank[i]] = i;
sort(cc+1, cc+n+1, cmp2);
build(1, 1, n);
for(int i = n; i > 0; i--){
int temp1 = query(1, 1, n, Rank[i]+1, n, 0);
int temp2 = query(1, 1, n, 1, Rank[i]-1, 1);
if(temp1 == -1 && temp2 == -1){
Insert(0, i, 0);
Insert(0, i+n, 0);
}
else if(temp1 == -1 && temp2 != -1){
Insert(fw[temp2]+n, i, get_d(i, fw[temp2]));
temp1 = query(1, 1, n, 1, temp2-1, 1);
if(temp1 == -1) Insert(0, i+n, 0);
else Insert(fw[temp1], i+n, get_d(i, fw[temp1]));
}
else if(temp1 != -1 && temp2 == -1){
Insert(fw[temp1]+n, i, get_d(i, fw[temp1]));
temp2 = query(1, 1, n, temp1+1, n, 0);
if(temp2 == -1) Insert(0, i+n, 0);
else Insert(fw[temp2], i+n, get_d(i, fw[temp2]));
}
else{
int temp3;
if(get_d(i, fw[temp1]) < get_d(i, fw[temp2])){
Insert(fw[temp1]+n, i, get_d(i, fw[temp1]));
temp3 = query(1, 1, n, temp1+1, n, 0);
if(~ temp3 && get_d(i, fw[temp3]) < get_d(i, fw[temp2])) temp2 = temp3;
Insert(fw[temp2], i+n, get_d(i, fw[temp2]));
}
else{
Insert(fw[temp2]+n, i, get_d(i, fw[temp2]));
temp3 = query(1, 1, n, 1, temp2-1, 1);
if(~ temp3 && get_d(i, fw[temp3]) <= get_d(i, fw[temp1])) temp1 = temp3;
Insert(fw[temp1], i+n, get_d(i, fw[temp1]));
}
}
update(1, 1, n, Rank[i], Rank[i]);
}
Da();
scanf("%lld", &xx);
for(int i = 1; i <= n; i++){
int now = i + n;
x0 = xx;
for(int j = 19; j >= 0; j--){
LL distance = dep[now] - dep[f[j][now]];
if(distance <= x0){
now = f[j][now];
x0 -= distance;
}
}
LL sA = dA[i+n] - dA[now], sB = dep[i+n] - dep[now] - dA[i+n] + dA[now];
if(sB == 0){
if(rate == oo && cc[i].height > cc[ansx].height) ansx = i;
continue;
}
else if(rate == oo){
ansx = i;
rate = (double)sA / sB;
}
else if((double)sA / sB - rate < -Eps || ((fabs((double)sA / sB - rate) < Eps) && cc[i].height > cc[ansx].height)){
ansx = i;
rate = (double)sA / sB;
}
}
printf("%d\n", ansx);
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf("%d%lld", &s, &x0);
s += n;
int now = s;
for(int j = 19; j >= 0; j--){
LL distance = dep[now] - dep[f[j][now]];
if(distance <= x0){
now = f[j][now];
x0 -= distance;
}
}
LL sA = dA[s] - dA[now], sB = dep[s] - dep[now] - dA[s] + dA[now];
printf("%lld %lld\n", sA, sB);
}
return 0;
}
走过那个转角 仿佛便会回到
配合彼此步幅漫步的当初
拱形的樱花树
如今已是满树橘红
——《光芒》