#2585. 「APIO2018」新家

https://loj.ac/problem/2585

分析:

  线段树+二分。

  首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左端点,这里不太行。这里只需要统计是否全出现过,pre[i]为这个颜色的上一个位置,那么这也就说明了pre[i]+1这段区间没出现过,所以要求[r+1,n]这段区间的最小的pre都要大于等于l。于是这就是线段树区间查询最小值了。

  注意的是,每个点的pre有多个,每个叶子节点包含一个set,把所有的值插入进去,取最小值。用两个堆维护也可以,(取最小值,删除一个值)。

  然后就可以二分一个长度,然后查询[x-len,x+len]这段区间是否都有所有的颜色就行了。复杂度$O(nlog^2n)$

  一个log的做法,二分的过程放到了线段树上。  

代码:

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int N = ;
const int INF = 1e9; struct Node{
int opt, x, col, t;
Node() {}
Node(int a,int b,int c,int d) { opt = a, x = b, col = c, t = d; }
bool operator < (const Node &A) const {
return t == A.t ? opt > A.opt : t < A.t; // 如果时间相同,先加入,在询问,再删除
}
}A[N * ];
struct Heap{
priority_queue<int, vector<int>, greater<int> > q1, q2;
int size() { return q1.size() - q2.size(); }
void add(int x) { q1.push(x); }
void del(int x) { q2.push(x); }
int top() {
while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
return q1.top();
}
// multiset<int> s;
// int size() { return s.size(); }
// void add(int x) { s.insert(x); }
// void del(int x) { s.erase(s.find(x)); }
// int top() { return *s.begin(); }
}H[N];
multiset<int> S[N];
int ans[N], Id[N * ], ls[N * ], rs[N * ], Mn[N * ];
int n, k, m, Index_Heap, Index_Tree, Root, Now_Col; void update(int l,int r,int &rt,int p,int u,int v) {
if (!rt) rt = ++Index_Tree;
if (l == r) {
if (!Id[rt]) Id[rt] = ++Index_Heap;
Heap &now = H[Id[rt]];
if (u) now.add(u);
if (v) now.del(v);
Mn[rt] = now.size() ? now.top() : INF;
return ;
}
int mid = (l + r) >> ;
if (p <= mid) update(l, mid, ls[rt], p, u, v);
else update(mid + , r, rs[rt], p, u, v);
Mn[rt] = min(Mn[ls[rt]], Mn[rs[rt]]);
}
void add(const Node &now) {
multiset<int> &s = S[now.col];
multiset<int> :: iterator r = s.upper_bound(now.x), l = r; l--;
update(, INF, Root, *r, now.x, *l);
update(, INF, Root, now.x, *l, );
if (s.size() == ) Now_Col ++;
s.insert(now.x);
}
void del(const Node &now) {
multiset<int> &s = S[now.col];
s.erase(s.find(now.x));
if (s.size() == ) Now_Col --;
multiset<int> :: iterator r = s.upper_bound(now.x), l = r; l--;
update(, INF, Root, *r, *l, now.x);
update(, INF, Root, now.x, , *l);
}
int Ask(int p) {
if (Now_Col != k) return -;
int l = , r = INF, now = Root, ans = INF;
while (l != r) { // 模拟在线段树上走的过程
int mid = (l + r) >> , tmp = min(ans, Mn[rs[now]]);
if (p <= mid && tmp + mid >= p + p) ans = tmp, r = mid, now = ls[now];
else l = mid + , now = rs[now];
}
return l - p;
}
int main() {
n = read(), k = read(), m = read();
Mn[] = INF;
for (int i = ; i <= k; ++i) // 初始往INF处加入k个-INF
S[i].insert(-INF), S[i].insert(INF), update(, INF, Root, INF, -INF, );
int tot = ;
for (int i = ; i <= n; ++i) {
int x = read(), t = read(), a = read(), b = read();
A[++tot] = Node(, x, t, a); A[++tot] = Node(-, x, t, b);
}
for (int i = ; i <= m; ++i) {
int x = read(), t = read();
A[++tot] = Node(, x, i, t);
}
sort(A + , A + tot + );
for (int i = ; i <= tot; ++i) {
if (A[i].opt == ) add(A[i]);
else if (A[i].opt == -) del(A[i]);
else ans[A[i].col] = Ask(A[i].x);
}
for (int i = ; i <= m; ++i) printf("%d\n",ans[i]);
return ;
}

LOJ #2585. 「APIO2018」新家的更多相关文章

  1. LOJ 2585 「APIO2018」新家 ——线段树分治+二分答案

    题目:https://loj.ac/problem/2585 算答案的时候要二分! 这样的话,就是对于询问位置 x ,二分出一个最小的 mid 使得 [ x-mid , x+mid ] 里包含所有种类 ...

  2. 【LOJ】#2585. 「APIO2018」新家

    题解 成功把自己写自闭了 离散化之后再二分我是真不会算坐标啊我这个zz 先离散化所有坐标,然后对于每个位置维护一个最小前驱,然后线段树区间维护最小前驱 什么?位置一样?那就给每个大小为1的位置开个mu ...

  3. LOJ #2587「APIO2018」铁人两项

    是不是$ vector$存图非常慢啊...... 题意:求数对$(x,y,z)$的数量使得存在一条$x$到$z$的路径上经过$y$,要求$x,y,z$两两不同  LOJ #2587 $ Solutio ...

  4. LOJ 2586 「APIO2018」选圆圈——KD树

    题目:https://loj.ac/problem/2586 只会 19 分的暴力. y 都相等,仍然按直径从大到小做.如果当前圆没有被删除,那么用线段树把 [ x-r , x+r ] 都打上它的标记 ...

  5. LOJ 2587 「APIO2018」铁人两项——圆方树

    题目:https://loj.ac/problem/2587 先写了 47 分暴力. 对于 n<=50 的部分, n3 枚举三个点,把图的圆方树建出来,合法条件是 c 是 s -> f 路 ...

  6. 【刷题】LOJ 2587 「APIO2018」铁人两项

    题目描述 比特镇的路网由 \(m\) 条双向道路连接的 \(n\) 个交叉路口组成. 最近,比特镇获得了一场铁人两项锦标赛的主办权.这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段 ...

  7. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  8. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  9. Loj #2719. 「NOI2018」冒泡排序

    Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...

随机推荐

  1. sed.md

    SED sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用,功能不同凡响.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用s ...

  2. jQuery UI 实例 – 切换(Toggle)

    toggle()函数用于为每个匹配元素的click事件绑定轮流的处理函数. toggle()是一个特殊的事件函数,用于为匹配元素的click事件绑定多个事件处理函数.每次触发click事件时,togg ...

  3. Java50道经典习题-程序23 求岁数

    题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁.问第4个人岁数,他说比第3个人大2岁.问第三个人,又说比第2人大两岁.问第2个人,说比第1个人大两岁.最后问第一个人,他说是10岁.请问 ...

  4. MyBatis保存完整日期的解决方法

    在用mybatis时,对mysql数据库是datatime字段添加值是,发现添加成功后查看数据库字段值是,只有年月日有值,时分秒则为0来表示的,更改为java.sql.date,time等也不行,如果 ...

  5. HDU 1561 The more, The Better(树形dp之树形01背包)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1561 The more, The Better Time Limit: 6000/2000 MS (J ...

  6. Struts2+AJAX+JQuery 实现用户登入与注册功能

    要求:必备知识:JAVA/Struts2,JS/JQuery,HTML/CSS基础语法:开发环境:MyEclipse 10 关于UI部分请查看下列链接,有详细制作步骤: 利用:before和:afte ...

  7. pThread多线程demo

    #import "ViewController.h" #import <pthread.h> @interface ViewController () @end @im ...

  8. 在正文部分操作accordion内容展开和闭合

    $('#accordionid').accordion("select",0);             //展开第一个title $('#accordionid').accord ...

  9. ORM一对多查询

    现有两张表,一张书籍表(Book),一张作者表(Author) 现在想查询出书本信息和书本的作者 book=Book.objects.get(name="python") book ...

  10. Java并发编程(十)死锁

    哲学家进餐问题 并发执行带来的最棘手的问题莫过于死锁了,死锁问题中最经典的案例就是哲学家进餐问题:5个哲学家坐在一个桌子上,桌子上有5根筷子,每个哲学家的左手边和右手边各有一根筷子.示意图如下: 哲学 ...