洛谷P4632 [APIO2018] New Home 新家(动态开节点线段树 二分答案 扫描线 set)
题意
Sol
这题没有想象中的那么难,但也绝对不简单。
首先把所有的询问离线,按照出现的顺序。维护时间轴来处理每个询问
对于每个询问\((x_i, y_i)\),可以二分答案\(mid\)。
问题转化为对于所有\(a_i \leqslant y_i \leqslant b_i\)的商店,\((x - mid, x + mid)\)内是否所有类型的商店都出现过
若都出现过,减小\(mid\),否则增大\(mid\)
现在有两个问题:
如何维护当前可行的所有商店,以及我们需要的信息
如何判断\((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)的更多相关文章
- 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)
题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...
- 洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)
题意 题目链接 Sol \(f[i][j]\)表示前\(i\)行\(j\)列的贡献,转移的时候枚举从哪里转移而来,复杂度\(O(n^4)\) 然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答 ...
- 洛谷P3994 Highway(树形DP+斜率优化+可持久化线段树/二分)
有点类似NOI2014购票 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ 这个显然是可以斜率优化的... $\frac {f(j)-f(k)}{dep_j ...
- 洛谷P3960 列队(动态开节点线段树)
题意 题目链接 Sol 看不懂splay..,看不懂树状数组... 只会暴力动态开节点线段树 观察之后不难发现,我们对于行和列需要支持的操作都是相同的:找到第\(k\)大的元素并删除,在末尾插入一个元 ...
- 洛谷P4344 脑洞治疗仪 [SHOI2015] 线段树+二分答案/分块
!!!一道巨恶心的数据结构题,做完当场爆炸:) 首先,如果你用位运算的时候不小心<<打成>>了,你就可以像我一样陷入疯狂的死循环改半个小时 然后,如果你改出来之后忘记把陷入死循 ...
- 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树
洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...
- 洛谷 P3258 [JLOI2014]松鼠的新家(树链剖分)
题目描述松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来 ...
- 洛谷 P3258 [JLOI2014]松鼠的新家 解题报告
P3258 [JLOI2014]松鼠的新家 题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他 ...
- 洛谷P3258 [JLOI2014]松鼠的新家(树上差分+树剖)
题目描述 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前 ...
随机推荐
- string[] 转换为 int[]
string[] ke=...... int[] output = Array.ConvertAll<string, int>(ke,delegate (string s) { retur ...
- Zookeeper客户端对比选择_4
Zookeeper客户端对比选择 本文思维导图 使用框架的好处是自带一套实用的API,但是Zookeeper虽然非常强大,但是社区却安静的可怕,版本更新较慢,下面会先从zookeeper原生API的不 ...
- 浅谈C#中的委托、事件与异步
从刚接触c#编程到现在,差不多快有一年的时间了.在学习过程中,有很多地方始终似是而非,直到最近才弄明白. 本文将先介绍用法,后评断功能. 一.委托 基本用法: 1.声明一个委托类型.委托就像是‘类'一 ...
- 20165224 陆艺杰 Exp5 MSF基础应用
用自己的话解释什么是exploit,payload,encode Exploit是攻击的行为 Payload是一段植入目标机的简短的带功能的恶意代码 Encode是编码,用于更改恶意代码,编码特征码检 ...
- RPC 定义 和 原理
一.RPC 1. RPC是什么 RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. ...
- python的下划线
首先是单下划线开头,这个被常用于模块中,在一个模块中以单下划线开头的变量和函数被默认当作内部函数,如果使用 from a_module import * 导入时,这部分变量和函数不会被导入.不过值得注 ...
- codeforces1097D Makoto and a Blackboard 数学+期望dp
题目传送门 题目大意: 给出一个n和k,每次操作可以把n等概率的变成自己的某一个因数,(6可以变成1,2,3,6,并且概率相等),问经过k次操作后,期望是多少? 思路:数学和期望dp 好题好题!! ...
- 动态规划算法解析网页中的内容 JavaScript
function getInfo(html){ //去掉注释 html=html.replace(/<!--.+?-->/g,"") var arrP=[] var r ...
- fixed
之前我们见的fixed就是固定在低端.后来我遇到一个问题 如果 这样写: div{ position: fixed; left: 0; bottom: 0; } 内容吧div撑开,有多少算多少 di ...
- 从零开始学Linux(11)--more
more命令,是将文件的内容从上到下显示在屏幕上. more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空格键(space)就往下一页显示,按 b 键就会往回(back)一页显示,而且还 ...