[BZOJ2402]陶陶的难题II

Orz福州一中lyk。

首先我们来看,对于每个询问,如果答案的分母是固定的话,那么变成了在树链上找两个点使得Yi+Qj最大,这个很简单吧,直接树链剖分然后线段树里面找两个最大的数。 好,其实,这个跟这题一点关系都没有。

但是我们可以二分答案`k`,那么问题变成了判定是否存在i, j使得
    (Yi+Qj)/(Xi+Pj) >= k
也就是
    k*Xi-Yi + k*Pj-Qj <= 0
因为加号左右基本是一样的,考虑左边,右边同理。不妨令
    B = k*Xi-Yi
也就是
    Yi = k*Xi - B
嗯,很像斜率优化,把(Xi, Yi)看成座标系上的点,那么实际上我们就是要在一堆点里面找到一个点使得截距-B最大

接下来就是维护凸壳了,我们只要先做树链剖分,顺便求出dfs序,然后按照dfs序建线段树。线段树节点[L, R]表示dfs序上[L, R]的点构成的上凸壳(其实这题要维护两个上凸壳,因为完全独立,所以也没什么好说的)。然后这题就解决了。

错误

我出现一个比较沙茶的错误,没有发现其实建线段树的时候,插入[L, R]这段的点的横座标并没有升序,结果样例最后一个点死活都是错的……debug了才发现……后来就用类似于归并的东西把所有点排序了。

代码

bzoj2402陶陶的难题II
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <cstdio>
#include <cstring>
#include <algorithm>

const int MAXN = 30001;
struct Edge
{
  int v;
  Edge *next;
} g[MAXN*2], *header[MAXN];
void AddEdge(const int x, const int y)
{
  static int LinkSize = 0;
  Edge* const node = g+(LinkSize++);
  node->v = y;
  node->next = header[x];
  header[x] = node;
}
struct Point
{
  double x, y;
  Point() { }
  Point(const double a, const double b): x(a), y(b) { }
};
inline bool operator<(const Point& lhs, const Point& rhs) { return lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y); }
const Point O(0, 0);
inline double cross(const Point& A, const Point& B, const Point& C, const Point& D)
{
  const double x1 = B.x-A.x, y1 = B.y-A.y;
  const double x2 = D.x-C.x, y2 = D.y-C.y;
  return x1*y2-x2*y1;
}
//------Segment Tree
struct Segment { Point *Q; int tail; };
Point point[2][MAXN], _memory[MAXN*32], *_memory_top = _memory;
Segment s[2][MAXN*4];
int _t, _st, _ed, n, m, pos[2][MAXN];
double _k;
double _query(const int p, const int lef, const int rig)
{
  if (_st <= lef && rig <= _ed)
  {
    Point *Q = s[_t][p].Q;
    int l = 0, r = s[_t][p].tail-1;
    while (l <= r)
    {
      const int m = (l+r)/2;
      if (cross(Q[m], Q[m+1], O, Point(1, _k)) >= -1e-6) r = m-1;
      else l = m+1;
    }
    return Q[l].x * _k - Q[l].y;
  }
  const int mid = (lef+rig)/2;
  double res = 1e10;
  if (_st <= mid) res = _query(p*2, lef, mid);
  if (mid+1 <= _ed) res = std::min(res, _query(p*2+1, mid+1, rig));
  return res;
}
inline bool cmp(const int lhs, const int rhs) { return point[lhs] < point[rhs]; }
void Build(const int p, const int lef, const int rig)
{
  static int tmp[2][MAXN];
  const int mid = (lef+rig)/2;
  if (lef != rig)
  {
    Build(p*2, lef, mid);
    Build(p*2+1, mid+1, rig);
  }
  for (int t = 0; t < 2; ++t)
  {
    for (int i = lef, l1 = lef, l2 = mid+1; i <= rig; ++i)
      if (l2 > rig || (l1 <= mid && point[t][pos[t][l1]] < point[t][pos[t][l2]])) tmp[t][i] = pos[t][l1++];
      else tmp[t][i] = pos[t][l2++];
    memcpy(pos[t]+lef, tmp[t]+lef, sizeof(*tmp[t])*(rig-lef+1));
    Point *&Q = s[t][p].Q;
    int& tail = s[t][p].tail;
    Q = _memory_top, tail = -1, _memory_top += rig-lef+1;
    for (int i = lef; i <= rig; ++i)
    {
      while (tail >= 1 && cross(Q[tail], Q[tail-1], Q[tail], point[t][pos[t][i]]) <= 1e-6) --tail;
      Q[++tail] = point[t][pos[t][i]];
    }
  }
}
inline double Query(const int t, const int lef, const int rig, const double k)
{
  _t = t, _st = lef, _ed = rig, _k = k;
  return _query(1, 1, n);
}
//------Heavy Light Decomposition
typedef int Array[MAXN];
Array number, dfn, hson, top, parent, depth, size;
void dfs1(const int u)
{
  size[u] = 1;
  for (Edge *e = header[u]; e; e = e->next)
    if (e->v != parent[u])
    {
      parent[e->v] = u;
      depth[e->v] = depth[u]+1;
      dfs1(e->v);
      size[u] += size[e->v];
      if (size[e->v] > size[hson[u]])
        hson[u] = e->v;
    }
}
void dfs2(const int u, const int tp)
{
  static int timestamp;
  number[u] = ++timestamp, dfn[timestamp] = u, top[u] = tp;
  if (hson[u])
    dfs2(hson[u], tp);
  for (Edge *e = header[u]; e; e = e->next)
    if (e->v != parent[u] && e->v != hson[u])
      dfs2(e->v, e->v);
}
double query(int a, int b, double k)
{
  double ans0 = 1e10, ans1 = 1e10;
  for (int ta = top[a], tb = top[b]; ta != tb; a = parent[ta], ta = top[a])
  {
    if (depth[ta] < depth[tb])
      std::swap(ta, tb), std::swap(a, b);
    ans0 = std::min(ans0, Query(0, number[ta], number[a], k));
    ans1 = std::min(ans1, Query(1, number[ta], number[a], k));
  }
  if (depth[a] > depth[b]) std::swap(a, b);
  ans0 = std::min(ans0, Query(0, number[a], number[b], k));
  ans1 = std::min(ans1, Query(1, number[a], number[b], k));
  return ans0+ans1;
}

int main()
{
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) scanf("%lf", &point[0][i].x);
  for (int i = 1; i <= n; ++i) scanf("%lf", &point[0][i].y);
  for (int i = 1; i <= n; ++i) scanf("%lf", &point[1][i].x);
  for (int i = 1; i <= n; ++i) scanf("%lf", &point[1][i].y);
  for (int i = 1, x, y; i < n; ++i)
  {
    scanf("%d%d", &x, &y);
    AddEdge(x, y);
    AddEdge(y, x);
  }
  dfs1(1);
  dfs2(1, 1);
  for (int i = 1; i <= n; ++i) pos[0][i] = pos[1][i] = dfn[i];
  Build(1, 1, n);
  scanf("%d", &m);
  for (int i = 0, x, y; i < m; ++i)
  {
    scanf("%d%d", &x, &y);
    double lef = 0, rig = 1e10;
    while (rig-lef > 1e-8)
    {
      const double mid = (lef+rig)/2;
      if (query(x, y, mid) >= -1e-6) rig = mid;
      else lef = mid;
    }
    printf("%.4f\n", rig);
  }
}

Comments