题意

题目链接

Sol

这题没有想象中的那么难,但也绝对不简单。

首先把所有的询问离线,按照出现的顺序。维护时间轴来处理每个询问

对于每个询问\((x_i, y_i)\),可以二分答案\(mid\)。

问题转化为对于所有\(a_i \leqslant y_i \leqslant b_i\)的商店,\((x - mid, x + mid)\)内是否所有类型的商店都出现过

若都出现过,减小\(mid\),否则增大\(mid\)

现在有两个问题:

  1. 如何维护当前可行的所有商店,以及我们需要的信息

  2. 如何判断\((x - mid, x + mid)\)内是否所有类型的商店都出现过

显然问题1依赖于问题2

对于第二个问题,一种方法是直接树套树区间数颜色,另一个巧妙的方法是定义\(pre_i\)表示和\(i\)号位置类型相同的商店中,\(x\)坐标小于\(i\)的第一个位置

若\(mid\)是合法的,一定存在一个位置\(p \in (x + mid + 1, N)\)满足\(pre_p < x - mid\)

那么直接用线段树维护\(pre\)的最小值即可。

线段树应该动态开点,当然你也可以头铁写离散化然后就需要考虑各种边界问题。。

问题1实际上我们只需要维护好\(pre\)即可

一个显然的想法是直接开\(30w\)个set维护每个类型

加入 / 删除的时候只会影响到\(3\)个位置

时间复杂度:\(O(nlog^2n)\),单次询问的复杂度为\(O(logn)\)

需要注意一个细节,由于是在线段树上二分,所以二分的边界应该与线段树相同

#include<bits/stdc++.h>
#define mit multiset<int>::iterator
using namespace std;
const int MAXN = 3e5 + 10, L = 1e9, Lim = (1 << 22) + 1;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N, K, Q, cnt, rt, ans[MAXN];
multiset<int> op[MAXN];//val of each type
multiset<int> s[Lim];//
int Mn[Lim], ls[Lim], rs[Lim];
struct Query {
int ti, opt, x;// time type pos
bool operator < (const Query &rhs) const {
return ti == rhs.ti ? opt < rhs.opt : ti < rhs.ti;
}
}q[MAXN * 3 + 10];
int tot = 0;
void Erase(multiset<int> &s, int &val) {
mit t = s.find(val);
if(t != s.end()) s.erase(t);
}
void update(int k) {
Mn[k] = min(Mn[ls[k]], Mn[rs[k]]);
}
void Insert(int &k, int l, int r, int x, int New, int Old) {
if(!k) k = ++tot;
if(l == r) {
if(New) s[k].insert(New);
if(Old) Erase(s[k], Old); Mn[k] = (s[k].empty() ? L : *s[k].begin()); return ;
}
int mid = l + r >> 1;
if(x <= mid) Insert(ls[k], l, mid, x, New, Old);
else Insert(rs[k], mid + 1, r, x, New, Old);
update(k);
}
int query(int x) {
int l = 0, r = L, mid, now = rt, lim = L, ans;
while(l < r) {
int mid = l + r >> 1, mn = min(Mn[rs[now]], lim);
if((x > mid) || (mid - x < x - mn))
l = mid + 1, now = rs[now], ans = mid;
else r = mid, now = ls[now], lim = mn;
}
return l - x;
} N = read(); K = read(); Q = read(); Mn[0] = L;
for(int i = 1; i <= N; i++) {
int x = read(), t = read(), a = read(), b = read();
q[++cnt] = (Query) {a, t, x};
q[++cnt] = (Query) {b + 1, -t, x};
}
for(int i = 1; i <= Q; i++) {
int x = read(), y = read();
q[++cnt] = (Query) {y, N + i, x};
}
sort(q + 1, q + cnt + 1); int Now = 0;// now type num
for(int i = 1; i <= K; i++) op[i].insert(L), op[i].insert(-L), Insert(rt, 0, L, L, -L, 0);
for(int i = 1; i <= cnt; i++) {
int ty = q[i].opt;
if(ty > N)
ans[ty - N] = Now < K ? - 1 : query(q[i].x);
else if(ty > 0) { // add
mit t = op[ty].upper_bound(q[i].x), r = t--;
Insert(rt, 0, L, *r, q[i].x, *t);
Insert(rt, 0, L, q[i].x, *t, 0);
if(op[ty].size() == 2) Now++;
op[ty].insert(q[i].x); } else {
ty = -ty;
mit t = op[ty].upper_bound(q[i].x), r = t--; t--;
Insert(rt, 0, L, *r, *t, q[i].x);
Insert(rt, 0, L, q[i].x, 0, *t);
Erase(op[ty], q[i].x);
if(op[ty].size() == 2) Now--;
}
}
for(int i = 1; i <= Q; i++) printf("%d\n", ans[i]);
return 0;
}

洛谷P4632 [APIO2018] New Home 新家(动态开节点线段树 二分答案 扫描线 set)的更多相关文章

  1. 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)

    题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...

  2. 洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)

    题意 题目链接 Sol \(f[i][j]\)表示前\(i\)行\(j\)列的贡献,转移的时候枚举从哪里转移而来,复杂度\(O(n^4)\) 然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答 ...

  3. 洛谷P3994 Highway(树形DP+斜率优化+可持久化线段树/二分)

    有点类似NOI2014购票 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ 这个显然是可以斜率优化的... $\frac {f(j)-f(k)}{dep_j ...

  4. 洛谷P3960 列队(动态开节点线段树)

    题意 题目链接 Sol 看不懂splay..,看不懂树状数组... 只会暴力动态开节点线段树 观察之后不难发现,我们对于行和列需要支持的操作都是相同的:找到第\(k\)大的元素并删除,在末尾插入一个元 ...

  5. 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块

    !!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...

  6. 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树

    洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...

  7. 洛谷 P3258 [JLOI2014]松鼠的新家(树链剖分)

    题目描述松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来 ...

  8. 洛谷 P3258 [JLOI2014]松鼠的新家 解题报告

    P3258 [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他 ...

  9. 洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)

    题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前 ...

随机推荐

  1. Python——变量的引用和函数的参数和返回值的传递方式

    变量的引用 在python中,所有的变量都是指向地址,变量本身不保存数据,而是保存数据在内存中的地址.我们用下面的程序来理解: a = 10 print(id(a)) a = 11 print(id( ...

  2. python3入门之列表和元组

    获得更多资料欢迎进入我的网站或者 csdn或者博客园 前面一张主要学习了Python的安装,以及第一个程序helloword的编写,以及简单的输入和输出函数,这章主要介绍序列,列表,元组 序列  这章 ...

  3. Python3之subprocess模块

    一.简介 subprocess最早在2.4版本引入.用来生成子进程,并可以通过管道连接他们的输入/输出/错误,以及获得他们的返回值. # subprocess用来替换多个旧模块和函数 os.syste ...

  4. 如何在UITableViewController上添加一个固定的视图

    最近在使用UITableViewController,想在上面添加一个固定的视图,不随ScrollView滑动而移动.最后找到2种解决办法,一种是计算TableView的偏移,调整视图的位置,不断更新 ...

  5. 针对animationend和transitionend多次执行的问题解决方案

    对于animationend事件来说的话,如果我们在外层添加这个事件监听,如果监听元素里面还有动画,则里面元素动画结束也会执行这个animationend事件.所以我们可以这样做: if(e.targ ...

  6. js 获取 屏幕 可用高度...

    document.documentElement.clientWidth 此方法适用于手机... document.documentElement.clientHeight (浏览器(手机或电脑)可用 ...

  7. 数据结构5.4_m元多项式的表示

    三元多项式表示如下: P(x,y,z) = x10y3z2 + 2x6y3z2 + 3x5y2z2 + x4y4z + 6x3y4z + 2yz + 15 然后对式子进行变形: P(x,y,z)=(( ...

  8. asp.net 同时执行js事件和代码事件 导出 excel

     onclick="return bnQuery_onclick()" onserverclick="bnQuery_ServerClick"   public ...

  9. 【AC自动机】【字符串】【字典树】AC自动机 学习笔记

    blog:www.wjyyy.top     AC自动机是一种毒瘤的方便的多模式串匹配算法.基于字典树,用到了类似KMP的思维.     AC自动机与KMP不同的是,AC自动机可以同时匹配多个模式串, ...

  10. [转] javascript中的变量和垃圾回收

    [From] http://www.imooc.com/article/4585 基本类型和引用类型 js中的变量虽然不区分类型,但是实际上Ecmascript包含两种类型,基本类型和引用类型. 基本 ...