[bzoj3489][kdtree]A simple rmq problem

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

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,但未重测

题解

发现kdt原来这么强大
原来想了好久可持久化线段树没想到,然后发现做不到。
然后就%了一发发现,原来还有这种操作。
设第i个元素前一个相同元素的位置是pre[i],后一个相同元素的位置是nex[i],元素值为sum[i]
对于l~r,其实就是要找一个l<=i<=r,其中sum[i]最大且pre[i]< l && nex[i]>r
建立三元组(i,pre[i],nex[i]),值即为sum[i]
那么其实就是搞一个三维空间了,三维空间内确定一个范围并找到这个范围内的最大值
切割多维空间就是kdtree啦
在kdtree里进行贪心查找并剪枝即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int d[3],mn[3],mx[3],lc,rc;//0 位置 1 pre 2 nex 
    int s,id,pre,nex,smax;
}tr[110000],root;
void upd(int now)
{
    int lc=tr[now].lc,rc=tr[now].rc;
    if(lc){for(int i=0;i<3;i++)tr[now].mx[i]=max(tr[now].mx[i],tr[lc].mx[i]),tr[now].mn[i]=min(tr[now].mn[i],tr[lc].mn[i]);tr[now].smax=max(tr[now].smax,tr[lc].smax);}
    if(rc){for(int i=0;i<3;i++)tr[now].mx[i]=max(tr[now].mx[i],tr[rc].mx[i]),tr[now].mn[i]=min(tr[now].mn[i],tr[rc].mn[i]);tr[now].smax=max(tr[now].smax,tr[rc].smax);}
}
int cmpd;
bool cmp(node n1,node n2){return n1.d[cmpd]<n2.d[cmpd];}
int bt(int l,int r,int d)
{
    int mid=(l+r)/2;cmpd=d;
    int now=mid;
    nth_element(tr+l,tr+mid,tr+r+1,cmp);
    tr[now].smax=tr[now].s;
    if(l<mid)tr[now].lc=bt(l,mid-1,(d+1)%3);
    if(mid<r)tr[now].rc=bt(mid+1,r,(d+1)%3);
    upd(now);
    return now;
}
int ql,qr,ans;
bool chk(int now)
{
    if(now==0)return false;
    if(tr[now].mx[0]<ql || tr[now].mn[0]>qr)return false;
    if(tr[now].mn[1]>=ql || tr[now].mx[2]<=qr)return false;
    return true;
}
void fd(int now)
{
    if(tr[now].mn[0]>=ql && tr[now].mx[0]<=qr && tr[now].mx[1]<ql && tr[now].mn[2]>qr)
    {
        ans=max(ans,tr[now].smax);
        return ;
    }
    if(tr[now].id>=ql && tr[now].id<=qr && tr[now].pre<ql && tr[now].nex>qr)ans=max(ans,tr[now].s);
    int sx=tr[tr[now].lc].smax,sy=tr[tr[now].rc].smax;
    if(sx>sy)
    {
        if(tr[now].lc && sx>ans && chk(tr[now].lc))fd(tr[now].lc);
        if(tr[now].rc && sy>ans && chk(tr[now].rc))fd(tr[now].rc);
    }
    else
    {
        if(tr[now].rc && sy>ans && chk(tr[now].rc))fd(tr[now].rc);
        if(tr[now].lc && sx>ans && chk(tr[now].lc))fd(tr[now].lc);
    }
}
int n,m,a[110000];
int P[110000],N[110000],lastans;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
    {
        tr[i].pre=tr[i].mn[1]=tr[i].mx[1]=tr[i].d[1]=P[a[i]];
        P[a[i]]=i;
    }
    memset(N,63,sizeof(N));
    for(int i=n;i>=1;i--)
    {
        tr[i].nex=tr[i].mn[2]=tr[i].mx[2]=tr[i].d[2]=N[a[i]];
        N[a[i]]=i;
    }
    for(int i=1;i<=n;i++)tr[i].s=a[i],tr[i].id=tr[i].d[0]=tr[i].mx[0]=tr[i].mn[0]=i;
    int root=bt(1,n,0);
    lastans=0;
    while(m--)
    {
        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);
        ans=0;fd(root);
        printf("%d\n",ans);lastans=ans;
    }
    return 0;
}
[BZOJ]3489 A simple rmq problem 主席树套树 3489: A simple rmq problemTime Limit: 40 Sec Memory Limit: 600 MB Submit: 2074 Solved: 708 [Submit][Status][Discuss] Description因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的 阅读详情

相关推荐

BZOJ3489】A simple rmq problem(kd-tree)

先考虑怎么样才能保证某个数只出现了一次。 对于序列中的某个数 a[i]a[i]a[i],我们求出上一个出现 a[i]a[i]a[i] 的位置为 pre[i]pre[i]pre[i],下一个出现 a[i]a[i]a[i] 的位置为 nxt[i]nxt[i]nxt[i]。 那么当询问的 lll、rrr 满足 pre[i]<l⩽ipre[i]<l\leqslant ipre[i]<l⩽...

ez_lcw的博客 292

bzoj 3489: A simple rmq problem k-d树思想大暴力

3489: A simple rmq problem Time Limit: 10 SecMemory Limit: 512 MBSubmit: 551Solved: 170[Submit][Status][Discuss] Description 因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次...

alex151194310的博客 186

ROS机器人开发:从入门到SLAM实战指南

机器人操作系统(ROS)作为开源的机器人开发框架,通过分布式节点架构实现了模块化设计,支持多种编程语言混合开发。其核心价值在于提供硬件抽象层和通信中间件,使开发者能快速构建复杂的机器人系统。在SLAM(同步定位与建图)等典型应用中,ROS的Topic/Service通信机制和功能包管理系统大幅降低了开发门槛。通过集成激光雷达驱动、GMapping算法等功能包,开发者可快速搭建完整的导航系统。环境配置方面,推荐使用鱼香ROS等工具实现一键部署,配合VSCode插件提升开发效率。对于实时性要求高的场景,可采用N

weixin_34259232的博客 462

BZOJ3489 A simple rmq problem 【可持久化树套树】*

BZOJ3489 A simple rmq problem Description 因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。 Input 第一行为两个整数N,M。M是询问数,N是序列的长度(N&lt;=100...

Dream_maker的博客 428

BZOJ_3489_ A simple rmq problem_KDTree

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

137

BZOJ 3489: A simple rmq problem KDtree

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

weixin_30362233的博客 83

BZOJ3489: A simple rmq problem kdtree

题意:给出一个长度为N的序列,M个询问,每次询问[L,R]中只出现过一次的最大数,不存在输出0,强制在线 N<=100000 M<=200000 卡时神器kdtree暴力水过。 用set预处理出每个元素i前面一个和它相等的元素pre[i]和后面一个和它相等的元素nex[i],则查询变为查询l<=pos<=r,pre < l,nex > r的元素中最大的,用三维kdtree 暴力就可以了。

リーインカーネイション 601

BZOJ 3489 A simple rmq problem 可持久化KDtree/二维线段树

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题意概述: 给出一个序列,每次询问一个序列区间中仅出现了一次的数字最大是多少,如果没有的话输出0。 N<=100000,M<=200000. 分析: 考试的时候YY了一个可持久化KDtree可惜没有打完(一开始想着一维做最后发现自己真是太...

diezhan7052的博客 424

[BZOJ3489]A simple rmq problem(kd-tree)

题目描述传送门题解三维kd-tree,分别是:这个点的位置,上一个相同数字的位置,下一个相同数字的位置 然后每一个有一个点权即为这一位上的数字,然后对于每一个子树维护位置的最大值和最小值,以及前面的最小值和后面的最大值,这样来判断、暴力即可 我刚开始的时候强行把点权塞到一维里去,然后吃惊地发现比3d要快一些,大概是因为某些时候按照点权这一维度划分能一次卡掉更多的点卡常技巧:能不维护的就不维护代码

zyf2000 958

BZOJ3489】A simple rmq problem

BZOJ3489】A simple rmq problem 题面 bzoj 题解 这个题不强制在线的话随便做啊。。。 考虑强制在线时怎么搞 预处理出一个位置上一个出现的相同数的位置\(pre\)与下一个位置\(nxt\) 则对于一个询问\([l,r]\) 一个位置\(i\)当且仅当\(pre_i<l\)且\(nxt_i>r\) 我们可以将一个位置看作一个点坐标为\((pre...

109

BZOJ3489 A simple rmq problem

设第i个数上一次出现的位置为lst[i],下一次出现的位置为nxt[i],那么第i个数可以用作更新答案的条件就是lst[i]r&&l 把lst,nxt和i分别看作三维坐标,问题就转化为了立方体求最大值 对于二维情况,我们可以用二维线段树搞,现在是三维,但是我们发现lst这一维在查询的时候左端点一直为0(我觉得用接地这个词语来形容特别形象-_-),所以我们可以通过可持久化来维护第一维,由于外层进

neither_nor 901

bzoj 3489: A simple rmq problem

Description 给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0, 强制在线。 Solution 设 \(pre[i]\) 表示 \(i\) 之前第一个与 \(a[i]\) 相同的位置 \(nxt[i]\) 表示 \(i\) 之后第一个与 \(a[i]\) 相同的位置 满足要求...

weixin_30426957的博客 64

BZOJ3489: A simple rmq problem

队友丢过来的毒瘤数据结构 ...KDtree模板题?告辞 不会KDtree ....那我只能考虑别的做法了 很显然 每个a[i] 都有上一个出现的位置last和下一个出现的位置next 对于查询的区间而言 当且仅当 [l,r]属于(last,next)的子集时并且了l<=i<=r这点的值才会产生贡献 因此 我们考虑主席树维护以pos为位置的历史版本 对于主席树中的[next,n]区...

weixin_30610755的博客 85

BZOJ - 3489 A simple rmq problem (K-D Tree 求给定区域内最大的点)

3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 512 MB Submit: 2838  Solved: 950 [Submit][Status][Discuss] Description 因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要...

LP_Cong 477

BZOJ.3489.A simple rmq problem(主席树 Heap)

题目链接 当时没用markdown写,可能看起来比较难受...可以复制到别的地方看比如DevC++。 \(Description\) 给定一个长为n的序列,多次询问[l,r]中最大的只出现一次的数。强制在线。 \(Solution\) 我也不知道该怎么说,反正就是预处理 建主席树,套堆/set,树i存l为i,r为[i,n]的答案(这样就是在某棵树上单点查maxv了)。 处理好最初的树,就可以利用...

weixin_30538029的博客 118

BZOJ】【3489】A simple rmq problem

KD-Tree(乱搞)   Orz zyf教给蒟蒻做法   蒟蒻并不会这题正解……(可持久化树套树?。。。Orz   对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足$ ( pre[i]<ql \ and \ nex[i]>qr\ and\ i \in [ql,qr] ) $   然后我们以(i,pre[i],n...

weixin_30617737的博客 138
上一篇: [bzoj2464][最短路]小明的游戏
下一篇: [bzoj1965][数论]SHUFFLE 洗牌
Rose_max
博客等级 码龄10年 97粉丝 610原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值