【CF 675D】 Tree Construction(离线2分+左右指针)
【CF 675D】 Tree Construction(离线二分+左右指针)
题目大意:建立二叉查找树。添加n个结点,输出添加第2~n个结点时 对应的父节点值(第1个点是根 无父节点 故不输出
【CF 675D】 Tree Construction(离线二分+左右指针)
首先要找到一个结论:对于已添加第1~(i-1)个结点的树,添加第i个结点v时,父亲一定是之前添加的值最接近v的点。即min(|u-v|)的那个u
这样会出现u < v或者u > v(及左边最靠近v和右边最靠近v的)两种 一定是选择最晚出现的u来作为v的父亲。
因为对于两个u此时已经加入到树中,而且在没有v之前,uL与uR是相邻(连)的。及其中一个为另一个父亲
其实也就是较早出现的为较晚出现的那个点的父亲。
在线处理的话,如果能很快插入的话,保证序列有序,每次插入一个点前,二分出该点的左右邻点,找出其中最晚出现的,即为该点父亲。
我用的vector超时了,看他们有用set过的。。。没试
后来想到秃头,想出一个离线处理的方法。
先把所有点存下来,并按点值从小到大排序,然后去个重。同时,每个点记录最早出现的时刻,还有左右邻点。
然后从第n次加入到第2次加入的点遍历(读取的时候预先备份一下即可
二分找到这个点,然后比较下左右邻点,选取所需要的即可。
如果找某个点时,发现再往前没有该点,把他左右邻点链接即可。即为将该点扣去。
代码如下:
#include <bits/stdc++.h> #define LL long long using namespace std; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; const double eps = 1e-8; struct Node { int x,mn,mx,l,r; bool operator < (const struct Node a)const { return x < a.x; } }; Node nd[100100]; int x[100100]; int ans[100100]; int tp = 0; int main() { int n,l,r,pos; scanf("%d",&n); for(int i = 0; i < n; ++i) { scanf("%d",&x[i]); <span style="white-space:pre"> </span>nd[i].x = x[i]; nd[i].mn = nd[i].mx = i; } sort(nd,nd+n); for(int i = 0; i < n; ++i) { if(tp == 0 || nd[i].x != nd[tp-1].x) { nd[tp++] = nd[i]; nd[tp-1].l = tp-2; nd[tp-1].r = tp; } else { nd[tp-1].mn = min(nd[tp-1].mn,nd[i].mn); nd[tp-1].mx = min(nd[tp-1].mn,nd[i].mx); } } for(int i = n-1; i > 0; --i) { l = 0, r = tp-1; while(l <= r) { int mid = (l+r)>>1; if(nd[mid].x == x[i]) { pos = mid; break; } else if(nd[mid].x > x[i]) r = mid-1; else l = mid+1; } if(nd[pos].l == -1) ans[i] = nd[nd[pos].r].x; else if(nd[pos].r == tp) ans[i] = nd[nd[pos].l].x; else if(nd[nd[pos].r].mn > nd[nd[pos].l].mn) ans[i] = nd[nd[pos].r].x; else ans[i] = nd[nd[pos].l].x; if(nd[pos].mn == i) { nd[nd[pos].l].r = nd[pos].r; nd[nd[pos].r].l = nd[pos].l; } } for(int i = 1; i < n; ++i) { if(i != 1) putchar(' '); printf("%d",ans[i]); } return 0; }