Address
- 洛谷 P4632
- BZOJ 5462
- UOJ #414
- LOJ #2585
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
#include
#include
#include
#include
#include
#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;
}