Address
Solution
自从来到 GZ 市来一直都鸽着没去调这题,几天之后才去调
最直接的
O
(
(
n
+
q
)
log
3
n
)
O((n+q)\log^3n)
O((n+q)log3n) 思路
- 看到我们的询问要求「最小值的最大值」,容易想到二分答案
- 具体地,假设我们在某个时间点,知道了这时候所有点的出现位置,那么二分答案
m
i
d
mid
mid 之后转化为判定性问题:区间
[
x
−
m
i
d
,
x
+
m
i
d
]
[x-mid,x+mid]
[x−mid,x+mid] 内所有点的颜色种类数是否为
k
k
k
- 当然,我们的关键是如何知道这时候所有点的出现位置
- 考虑把一个点的信息用两次事件描述
- (1)第
i
i
i 个点的颜色为
t
i
t_i
ti ,在
l
i
l_i
li 时刻于位置
x
i
x_i
xi 出现
- (2)第
i
i
i 个点在
r
i
+
1
r_i+1
ri+1 时刻消失
- 这样一个点可以拆成两个四元组:
(
x
i
,
t
i
,
l
i
,
1
)
(x_i,t_i,l_i,1)
(xi,ti,li,1)
(
x
i
,
t
i
,
r
i
+
1
,
−
1
)
(x_i,t_i,r_i+1,-1)
(xi,ti,ri+1,−1)
- (第四元为
1
1
1 表示出现,否则表示消失)
- 把所有的四元组按照第三元进行排序,询问也按照时间离线排序,使用扫描线从左到右扫时间轴,我们的问题转化成:带插入和删除点,求区间内出现的颜色种数
- 如果是静态的求区间颜色数,那么我们可以对每个位置维护一个
p
r
e
[
i
]
pre[i]
pre[i] ,表示
i
i
i 的左边,离
i
i
i 最近的,与
i
i
i 同色的位置
- 这样求
[
l
,
r
]
[l,r]
[l,r] 的区间颜色数,就转化成区间
[
l
,
r
]
[l,r]
[l,r] 内有多少个
p
r
e
<
l
pre<l
pre<l ,可以使用主席树解决
- 而此题需要支持插入和删除点,但我们很容易发现插入和删除点影响到的
p
r
e
pre
pre 值个数最多为
2
2
2 (插入 / 删除的点本身以及这个点的后继点)
- 我们可以想到对于每一种颜色维护一个以关键字为坐标位置的 set ,插入一种颜色时在 set 上处理下
p
r
e
pre
pre 值的变化之后,使用树状数组套权值线段树来维护
p
r
e
pre
pre 的值分布
- 复杂度
O
(
(
n
+
q
)
log
3
n
)
O((n+q)\log^3n)
O((n+q)log3n) (二分一个
log
\log
log ,树状数组套权值线段树两个
log
\log
log )
优化
- 冷静一下!!!!!
O
(
(
n
+
q
)
log
3
n
)
O((n+q)\log^3n)
O((n+q)log3n) 的复杂度在这题里显然是过不去的
- 冷静一下!!!!!我们只需要知道区间内是否出现所有
k
k
k 种颜色,而不要求区间内的颜色种数
- 首先,
各凭本事使用一些奇技淫巧可以求出一个
m
a
x
l
maxl
maxl ,表示出现所有
k
k
k 种颜色的所有区间的左端点最大值。可以发现它其实就是所有颜色出现的最右位置的最小值,用 set 维护即可 - 特殊地,如果某种颜色没出现过则
m
a
x
l
=
0
maxl=0
maxl=0
- 然后我们思考能不能给定一个
r
r
r ,求出一个最大的
l
l
l ,使得右端点为
r
r
r ,左端点为
l
l
l 的所有区间都包含所有
k
k
k 种颜色
- 显然这个
l
l
l 必须满足
l
≤
m
a
x
l
l\le maxl
l≤maxl
- 而在
l
≤
m
a
x
l
l\le maxl
l≤maxl 的限制下,区间
[
l
,
+
∞
)
[l,+\infty)
[l,+∞) 一定出现了所有颜色
- 我们接着考虑
[
l
,
r
]
[l,r]
[l,r] 出现所有颜色的条件,很容易发现这个条件是:对于任意的
i
∈
(
r
,
+
∞
)
i\in(r,+\infty)
i∈(r,+∞) 都必须有
p
r
e
i
≥
l
pre_i\ge l
prei≥l
- 于是我们得到
-
l
=
min
(
m
a
x
l
,
min
i
>
r
p
r
e
i
)
l=\min(maxl,\min_{i>r}pre_i)
l=min(maxl,i>rminprei)
- 同时注意判断无解的条件为
l
=
0
l=0
l=0
- 回到原问题,显然二分答案
m
i
d
mid
mid 之后
[
x
−
m
i
d
,
x
+
m
i
d
]
[x-mid,x+mid]
[x−mid,x+mid] 出现所有
k
k
k 种颜色的条件为
-
x
−
m
i
d
≤
min
(
m
a
x
l
,
min
i
>
x
+
m
i
d
p
r
e
i
)
x-mid\le\min(maxl,\min_{i>x+mid}pre_i)
x−mid≤min(maxl,i>x+midminprei)
- 注意离散化对坐标实际值的影响
- 这时候我们已经将复杂度去掉了一个
log
n
\log n
logn ,足以通过此题
- 当然我们还可以把这个二分答案改成在维护
p
r
e
pre
pre 最小值的线段树上二分,做到
O
(
(
n
+
q
)
log
n
)
O((n+q)\log n)
O((n+q)logn) 的优秀复杂度
- 此外,由于此题有重复坐标,所以还是有一定细节量的,注意 set 的关键字
Code
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef std::multiset<int>::iterator its;
template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}
template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}
const int N = 3e5 + 5, M = N << 1, L = M << 2, INF = 0x3f3f3f3f;
int n, k, q, m, real[M], minpre[L], ans[N], lst_col[N];
struct modify
{
int tm, pos, col, id;
} md[M];
inline bool comp(modify a, modify b)
{
return a.tm < b.tm;
}
struct query
{
int tm, pos, id;
} qr[N];
inline bool pmoc(query a, query b)
{
return a.tm < b.tm;
}
struct elem
{
int pos, id;
friend inline bool operator < (elem a, elem b)
{
return a.pos < b.pos || (a.pos == b.pos && a.id < b.id);
}
};
std::set<elem> cols[N];
std::multiset<int> pre[M], col_lst;
typedef std::set<elem>::iterator it;
void del_val(int pos, int val)
{
its pyz = pre[pos].find(val);
pre[pos].erase(pyz);
}
void change(int l, int r, int pos, int v, int p)
{
if (l == r) return (void) (minpre[p] = v);
int mid = l + r >> 1;
if (pos <= mid) change(l, mid, pos, v, p2);
else change(mid + 1, r, pos, v, p3);
minpre[p] = Min(minpre[p2], minpre[p3]);
}
int ask(int l, int r, int x, int ri, int p)
{
if (l == r) return ri && real[ri] + real[l]
>= (x << 1) ? l : l + 1;
int mid = l + r >> 1;
int delta = real[Min(ri, minpre[p3])] + real[mid];
if (Min(ri, minpre[p3]) && delta >= (x << 1))
return ask(l, mid, x, Min(ri, minpre[p3]), p2);
else return ask(mid + 1, r, x, ri, p3);
}
int __querymin(int l, int r, int s, int e, int p)
{
if (l == s && r == e) return minpre[p];
int mid = l + r >> 1;
if (e <= mid) return __querymin(l, mid, s, e, p2);
else if (s >= mid + 1) return __querymin(mid + 1, r, s, e, p3);
else return Min(__querymin(l, mid, s, mid, p2),
__querymin(mid + 1, r, mid + 1, e, p3));
}
void ins(int col, int pos, int id)
{
it pyz = cols[col].lower_bound((elem) {pos, id}), zzq = pyz;
int lst = zzq == cols[col].begin() ? 0 : (*--zzq).pos;
if (pyz != cols[col].end())
{
del_val(pyz->pos, lst);
pre[pyz->pos].insert(pos);
change(1, m, pyz->pos, pre[pyz->pos].empty() ? INF :
*pre[pyz->pos].begin(), 1);
}
pre[pos].insert(lst);
change(1, m, pos, pre[pos].empty() ? INF :
*pre[pos].begin(), 1);
cols[col].insert((elem) {pos, id});
its ysy = col_lst.find(lst_col[col]);
col_lst.erase(ysy);
int n_ysy = cols[col].empty() ? 0 :
(pyz = cols[col].end(), *--pyz).pos;
col_lst.insert(lst_col[col] = n_ysy);
}
void del(int col, int pos, int id)
{
it pyz = cols[col].find((elem) {pos, id}), zzq = pyz, zzt = pyz;
int lst = zzq == cols[col].begin() ? 0 : (*--zzq).pos;
del_val(pos, lst);
change(1, m, pos, pre[pos].empty() ? INF :
*pre[pos].begin(), 1);
if ((++zzt) != cols[col].end())
{
del_val(zzt->pos, pyz->pos);
pre[zzt->pos].insert(lst);
change(1, m, zzt->pos, pre[zzt->pos].empty() ? INF :
*pre[zzt->pos].begin(), 1);
}
cols[col].erase(pyz);
its ysy = col_lst.find(lst_col[col]);
col_lst.erase(ysy);
int n_ysy = cols[col].empty() ? 0 :
(pyz = cols[col].end(), *--pyz).pos;
col_lst.insert(lst_col[col] = n_ysy);
}
int querymin()
{
return *col_lst.begin();
}
int main()
{
memset(minpre, INF, sizeof(minpre));
int x, t, l, r, p = 1;
n = read(); k = read(); q = read();
for (int i = 1; i <= k; i++) col_lst.insert(lst_col[i] = 0);
for (int i = 1; i <= n; i++)
{
x = read(); t = read(); l = read(); r = read();
md[(i << 1) - 1] = (modify) {l, x, t, i};
md[i << 1] = (modify) {r + 1, x, t, -i};
real[++m] = x;
}
for (int i = 1; i <= q; i++)
x = read(), t = read(), qr[i] = (query) {t, x, i},
real[++m] = x;
std::sort(md + 1, md + (n << 1) + 1, comp);
std::sort(qr + 1, qr + q + 1, pmoc);
std::sort(real + 1, real + m + 1);
int tm = std::unique(real + 1, real + m + 1) - real - 1;
m = tm;
for (int i = 1; i <= (n << 1); i++)
md[i].pos = std::lower_bound
(real + 1, real + m + 1, md[i].pos) - real;
for (int i = 1; i <= q; i++)
qr[i].pos = std::lower_bound
(real + 1, real + m + 1, qr[i].pos) - real;
for (int i = 1; i <= q; i++)
{
while (p <= (n << 1) && md[p].tm <= qr[i].tm)
{
if (md[p].id > 0) ins(md[p].col, md[p].pos, md[p].id);
else del(md[p].col, md[p].pos, -md[p].id);
p++;
}
int tmp = querymin();
if (!tmp)
{
ans[qr[i].id] = -1; continue;
}
int t = ask(1, m, real[qr[i].pos], tmp, 1);
ans[qr[i].id] = t == m + 1 ? INF : real[t] - real[qr[i].pos];
if (t > 1)
{
int fstqwq = t == m + 1 ? tmp :
Min(tmp, __querymin(1, m, t, m, 1));
if (!fstqwq) continue;
ans[qr[i].id] = Min(ans[qr[i].id],
real[qr[i].pos] - real[fstqwq]);
}
}
for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
return 0;
}