[hdu4585]离线,并查集

题意:把一些数加到集合里面,每个数有两个属性,分别是编号和值,加进去之前询问集合里面距离自己“最近”的数的编号。最近的意思是值的差的绝对值最小,如果有相等的,则取值小的。并且已知所有数的id和value都是唯一的。

思路:把处理过程反过来,就变成了一次把一个点删除,删除可以用数组标记,而询问则转化为找某个数左边的第一个没标记的数和右边的第一个没被标记的数,由于删除是永久的,所以完全可以通过并查集来加速,标记也可以省略。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#pragma comment(linker, "/STACK:10240000,10240000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
#define fst first
#define snd second
pair<intint> node[123456];
int a[123456], b[6666666];
int fa1[123456], fa2[123456];
int getfa1(int u) { return u == fa1[u]? u : fa1[u] = getfa1(fa1[u]); }
int getfa2(int u) { return u == fa2[u]? u : fa2[u] = getfa2(fa2[u]); }
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt""r", stdin);
#endif // ONLINE_JUDGE
    int n;
    while (cin >> n, n) {
        vector<pair<intint> > out(n + 1);
        for (int i = 0; i <= n + 1; i ++) fa1[i] = fa2[i] = i;
        for (int i = 1; i <= n; i ++) {
            scanf("%d%d", &node[i].fst, &node[i].snd);
            a[i] = node[i].snd;
            b[a[i]] = node[i].fst;
        }
        sort(a + 1, a + 1 + n);
        for (int i = n; i > 1; i --) {
            int pos = lower_bound(a + 1, a + 1 + n, node[i].snd) - a;
            int L = getfa1(pos - 1), R = getfa2(pos + 1);
            int dif1 = 1e9, dif2 = 1e9;
            if (L > 0) dif1 = a[pos] - a[L];
            if (R <= n) dif2 = a[R] - a[pos];
            if (dif1 <= dif2) out[i] = make_pair(node[i].fst, b[a[L]]);
            else  out[i] = make_pair(node[i].fst, b[a[R]]);
            fa1[pos] = pos - 1;
            fa2[pos] = pos + 1;
        }
        out[1] = make_pair(node[1].fst, 1);
        for (int i = 1; i <= n; i ++) printf("%d %d ", out[i].fst, out[i].snd);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jklongint/p/4550717.html