Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >BZOJ 3489: A simple rmq problem(K-D Tree)

BZOJ 3489: A simple rmq problem(K-D Tree)

作者头像
attack
发布于 2019-02-13 02:05:52
发布于 2019-02-13 02:05:52
60900
代码可运行
举报
运行总次数:0
代码可运行

Time Limit: 40 Sec  Memory Limit: 512 MB

Submit: 2579  Solved: 888

[Submit][Status][Discuss]

Description

因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。

Input

第一行为两个整数N,M。M是询问数,N是序列的长度(N<=100000,M<=200000)

第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N

再下面M行,每行两个整数x,y,

询问区间[l,r]由下列规则产生(OIER都知道是怎样的吧>_<):

l=min((x+lastans)mod n+1,(y+lastans)mod n+1);

r=max((x+lastans)mod n+1,(y+lastans)mod n+1);

Lastans表示上一个询问的答案,一开始lastans为0

Output

一共M行,每行给出每个询问的答案。

Sample Input

10 10 6 4 9 10 9 10 9 4 10 4 3 8 10 1 3 4 9 4 8 1 7 8 2 9 1 1 7 3 9 9

Sample Output

4 10 10 0 0 10 0 4 0 4

HINT

注意出题人为了方便,input的第二行最后多了个空格。

2015.6.24新加数据一组,2016.7.9放至40S,600M,但未重测

Source

by zhzqkkk

网上又没有代码,自己yy了一上午才写出来

对于这道题目,我们记$pre_i$表示$i$号位置的元素前一次出现的位置

记$nxt_i$表示$i$号位置的元素后一次出现的位置

假设询问区间为$l,r$

那么$i$号位置满足条件,当且仅当$l<=i<=r, pre_i < l, nxt_i > r$

这样我们把$i, pre_i, nxt_i$看成一个三元组

然后用K-D tree加剪纸就行了

剪纸的时候考虑三种情况

  • 询问区间完全包含该节点及其左右儿子,直接通过打标记统计出最大值
  • 询问区间包含该节点但不包含其左右儿子,直接统计该节点对答案的贡献
  • 询问区间与该节点及其左右儿子不重合,直接return

有了这三种剪枝,而且此题没有插入,因此复杂度为$O(mn\sqrt{n})$

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<cstdio>
#include<cstring>
#include<algorithm>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)? EOF : *p1++)
using namespace std;
const int MAXN = 1e6 + 10;
char buf[1 << 21], *p1 = buf, *p2 = buf;
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, M, root, WD;
int Ql, Qr, lastans = 0;
struct Point {
    int x[4];
    bool operator < (const Point &rhs) const{
        return x[WD] < rhs.x[WD];
    }
}P[MAXN];
#define ls(x) T[x].ls
#define rs(x) T[x].rs
struct KDTree {
    int ls, rs, mn[3], mx[3], val;
    Point tp; 
}T[MAXN];
int a[MAXN], pre[MAXN], nxt[MAXN], happen[MAXN];
void update(int k) {
    for(int i = 0; i <= 2; i++) {
        T[k].mn[i] = T[k].mx[i] = T[k].tp.x[i];
        if(ls(k)) T[k].mn[i] = min(T[ls(k)].mn[i], T[k].mn[i]), T[k].mx[i] = max(T[ls(k)].mx[i], T[k].mx[i]);
        if(rs(k)) T[k].mn[i] = min(T[rs(k)].mn[i], T[k].mn[i]), T[k].mx[i] = max(T[rs(k)].mx[i], T[k].mx[i]);
    }
    T[k].val = max(T[k].tp.x[3], T[ls(k)].val);
    T[k].val = max(T[k].val, T[rs(k)].val);
}
int Build(int l, int r, int wd) {
    if(l > r) return 0;
    int mid = l + r >> 1; WD = wd;
    nth_element(P + l, P + mid, P + r + 1);
    T[mid].tp = P[mid];
    ls(mid) = Build(l, mid - 1, (wd + 1) % 3);
    rs(mid) = Build(mid + 1, r, (wd + 1) % 3);
    update(mid);
    return mid;
}
bool CheckInclude(int k) {
    if(T[k].mn[0] >= Ql && T[k].mx[0] <= Qr
    && T[k].mx[1] < Ql && T[k].mn[2] > Qr) return 1;
    return 0;
}
bool CheckCross(int k) {
    if(T[k].tp.x[0] >= Ql && T[k].tp.x[0] <= Qr
    && T[k].tp.x[1] < Ql && T[k].tp.x[2] > Qr) return 1;
    return 0;    
} 
bool CheckNotCross(int k) {
    if(T[k].mn[1] >= Ql || T[k].mx[2] <= Qr || T[k].mx[0] < Ql  || T[k].mn[0] > Qr) return 1;
    return 0; 
}
void Query(int k) {
    if(CheckInclude(k)) {
        lastans = max(lastans, T[k].val); return ; //矩形完全包含在查询区间内 
    }
    if(CheckCross(k)) 
        lastans = max(lastans, T[k].tp.x[3]); //相交 
    if(CheckNotCross(k)) return ; // 没有重合的地方 
    int disl = T[ls(k)].val, disr = T[rs(k)].val;
    if(disl > disr) {
        if(disl > lastans) Query(ls(k));
        if(disr > lastans) Query(rs(k));
    }
    else {
        if(disr > lastans) Query(rs(k));
        if(disl > lastans) Query(ls(k));
    }
}
int main() {
    #ifdef WIN32
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    #endif
    N = read(); M = read();
    for(int i = 1; i <= N; i++) a[i] = read();
    for(int i = 1; i <= N; i++) pre[i] = happen[a[i]], happen[a[i]] = i;
    for(int i = 1; i <= N; i++) happen[a[i]] = N + 1;
    for(int i = N; i >= 1; i--) nxt[i] = happen[a[i]], happen[a[i]] = i;
    for(int i = 1; i <= N; i++)
        P[i] = (Point) {i, pre[i], nxt[i], a[i]};
    root = Build(1, N, 0);
    for(int i = 1; i <= M; i++) {
        int x = read(), y = read(); 
        Ql = min((x + lastans) % N + 1,(y + lastans) % N + 1);
        Qr = max((x + lastans) % N + 1,(y + lastans) % N + 1);
        lastans = 0;
        Query(root);
        printf("%d\n", lastans);
    }
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-05-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
BZOJ 2648: SJY摆棋子(K-D Tree)
Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 6051  Solved: 2113 [Submit][Status][Discuss] Description 这天,SJY显得无聊。在家自己玩。在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000
attack
2018/05/30
5120
BZOJ 4520: [Cqoi2016]K远点对(k-d tree)
Time Limit: 30 Sec  Memory Limit: 512 MB Submit: 1162  Solved: 618 [Submit][Status][Discuss] Description 已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。 Input 输入文件第一行为用空格隔开的两个整数 N, K。接下来 N 行,每行两个整数 X,Y,表示一个点 的坐标。1 < =  N < =  100000, 1 < =  K < =  100, K < =  N*(N−1)/2 , 0
attack
2018/05/30
5880
BZOJ 3053: The Closest M Points(K-D Tree)
Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1235  Solved: 418 [Submit][Status][Discuss] Description The course of Software Design and Development Practice is objectionable. ZLC is facing a serious problem .There are many points in K-dimensional space
attack
2018/05/30
3540
BZOJ 1941: [Sdoi2010]Hide and Seek(k-d Tree)
Time Limit: 16 Sec  Memory Limit: 162 MB Submit: 1712  Solved: 932 [Submit][Status][Discuss] Description 小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏---捉迷藏。 但是,他们觉得,玩普通的捉迷藏没什么意思,还是不够寂寞,于是,他们决定玩寂寞无比的螃蟹版捉迷藏,顾名思义,就是说
attack
2018/05/30
6850
洛谷P3722 [AH2017/HNOI2017]影魔(线段树 set spaly)
题意 题目链接 Sol 这题好毒瘤啊。。 首先要观察到几个性质: 将最小值旋转到根相当于把右子树变为祖先的左子树,然后将原来的根变为当前最小值 上述操作对深度的影响相当于右子树不变,其他的位置-1 然后就可以做了,把询问离线之后离散化一下,建一棵权值线段树表示每个值对应的深度 同时用set维护出已经加入的值 每次先找到后继,看一下有没有左孩子,如果有的话说明前驱一定没有右孩子。 注意随时更新信息 复杂度\(O(nlogn)\) #include<bits/stdc++.h> #define Pair pa
attack
2019/03/08
3540
BZOJ4373: 算术天才⑨与等差数列(线段树 hash?)
第三条后面的可以直接推式子推出来(\(\sum_{i = 1}^n i^2 = \frac{n(n+1)(2n+1)}{6}\))
attack
2019/03/01
3820
BZOJ3585: mex(主席树)
Description   有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。 Input   第一行n,m。   第二行为n个数。   从第三行开始,每行一个询问l,r。 Output   一行一个数,表示每个询问的答案。 Sample Input 5 5 2 1 0 2 1 3 3 2 3 2 4 1 2 3 5 Sample Output 1 2 3 0 3 HINT 数据规模和约定   对于100%的数据:   
attack
2018/04/10
6550
cf666E. Forensic Examination(广义后缀自动机 线段树合并)
考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下最长能匹配的位置,对于每个以\(i\)位置为右端点的询问我们需要找到\(len\)最小的状态满足\(len[sta] >= pr - pl + 1\)(这部分把每个以\(i\)为端点的询问排序后暴力跳即可,复杂度\(O(n \sqrt{n})\))。那么现在的问题就是对于每个状态,如何知道他在每个\(T_i\)中的出现次数。
attack
2019/03/14
6330
洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
显然一个串能更新答案的区间是\([len_{fa_{x}} + 1, len_x]\),方案数就相当于是从\(siz_x\)里面选两个,也就是\(\frac{siz_x (siz_x - 1)}{2}\)
attack
2019/03/06
4410
cf1136E. Nastya Hasn't Written a Legend(二分 线段树)
显然从一个位置开始能影响到的位置是单调的,而且这些位置的每个改变量都是\((a_i + x) + \sum_{t=i}^{j-1} k_t\)
attack
2019/03/20
4690
BZOJ3413: 匹配(后缀自动机 线段树合并)
首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数。(画一画就出来了)
attack
2019/03/14
5120
洛谷P4069 [SDOI2016]游戏(李超线段树)
题意 题目链接 Sol 这题细节好多啊qwq。。稍不留神写出一个小bug就要调1h+。。 思路就不多说了,把询问区间拆成两段就是李超线段树板子题了。 关于dis的问题可以直接维护。 // luogu-judger-enable-o2 /* 李超线段树板子题 */ #include<bits/stdc++.h> #define Pair pair<int, int> #define MP(x, y) make_pair(x, y) #define fi first #define se second #d
attack
2019/03/04
4900
loj#6029. 「雅礼集训 2017 Day1」市场(线段树)
除法是不能打标记的,所以只能暴力递归。这里我们加一个剪枝:如果区间内最大最小值的改变量都相同的话,就变成区间减。
attack
2019/03/19
5780
P2605 [ZJOI2010]基站选址
题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入输出格式 输入格式: 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包
attack
2018/04/12
9590
洛谷P3722 [AH2017/HNOI2017]影魔(线段树)
对于本题而言,我们可以预处理出每个位置左边第一个比他大的位置\(l_i\)以及右边第一个比他大的位置\(r_i\)
attack
2019/03/11
3370
BZOJ4299: Codechef FRBSUM(主席树)
那么若$a[i + 1] > s_i + 1$,那么$a[i + 1]$不能被拼成
attack
2018/09/17
3590
洛谷P2633 Count on a tree
题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。 输入输出格式 输入格式: 第一行两个整数N,M。 第二行有N个整数,其中第i个整数表示点i的权值。 后面N-1行每行两个整数(x,y),表示点x到点y有一条边。 最后M行每行两个整数(u,v,k),表示一组询问。 输出格式: M行,表示每个询问的答案。 输入输出样例 输入样例#1:  8 5
attack
2018/04/11
4930
BZOJ4355: Play with sequence(吉司机线段树)
这题最坑的地方在于对于操作1,$C >= 0$, 操作2中需要对0取max,$a[i] >= 0$,这不就是统计最小值出现的次数么??
attack
2018/09/21
6650
洛谷P5108 仰望半月的夜空(后缀数组)
warning:下面这个做法只有95分,本地拍了1w+组都没找到错误我表示十分无能为力
attack
2019/04/01
3590
BZOJ3165: [Heoi2013]Segment(李超线段树)
题意 题目链接 Sol 李超线段树板子题。具体原理就不讲了。 一开始自己yy着写差点写自闭都快把叉积搬出来了。。。 后来看了下litble的写法才发现原来可以写的这么清晰简洁Orz #include<bits/stdc++.h> #define pdd pair<double, double> #define MP make_pair #define fi first #define se second using namespace std; const int MAXN = 1e6 + 10, Li
attack
2019/03/04
4240
推荐阅读
相关推荐
BZOJ 2648: SJY摆棋子(K-D Tree)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验