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

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

BZOJ3489 A simple rmq problem


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


还是吐槽一下BZOJ的土豆(评测机),什么数组开小了显示WA,数组开大一点就CE,这可真是友好,请问RE呢?MLE呢?????????


然后讲讲这题思路
一开始看到以为是树套树套树
蒙圈了
想了⑩分钟无果然后就去看题解
结果发现可以用排序的方法把三维降成两维

只不过嘛。需要可持久化
不过我们不虚
可持久化树套树不就是在可持久化线段树上多加一句插入吗理直气壮
首先我们考虑一个数在区间内出现一次的充要条件是什么
1.l<=posi<=r1.l<=posi<=r
2.l>prei2.l>prei
3.r<nxti3.r<nxti
其中pos表示位置,pre和nxt分别表示上一个和下一个这个数出现的位置
很妙啊
我们考虑以pre为键值排序
然后我们发现我们查询的区间变成了一个前缀
所以就将pre可持久化一下
然后我们又发现nxt也是我们的限制条件
所以我们在外层的树上以nxt为下标,所以查询就变成了在线段树上查询后缀

但是还有pos的限制怎么办?
所以就把内层树搞成原序列的位置就好了
然后查询的时候非常套路地二分一下pre的位置,剩下的该怎么查怎么查


#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')w=-1,c=getchar();
    while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
    return ans*w;
}
#define N 100010
int n,m,lastans=0;
struct Node{int val,pos,pre,nxt;}p[N];
bool cmp(Node a,Node b){return a.pre<b.pre;}
//inside 原序列顺序
int ld[N*350],rd[N*350],maxv[N*350];
int cnt=0;
void insert(int &t,int last,int l,int r,int pos,int vl){
    t=++cnt;
    maxv[t]=max(vl,maxv[last]);
    if(l==r)return;
    ld[t]=ld[last];
    rd[t]=rd[last];
    int mid=(l+r)>>1;
    if(pos<=mid)insert(ld[t],ld[last],l,mid,pos,vl);
    else insert(rd[t],rd[last],mid+1,r,pos,vl);
}
int query(int t,int l,int r,int L,int R){
    if(!t)return 0;
    if(L<=l&&r<=R)return maxv[t];
    int mid=(l+r)>>1;
    if(R<=mid)return query(ld[t],l,mid,L,R);
    if(L>mid)return query(rd[t],mid+1,r,L,R);
    return max(query(ld[t],l,mid,L,R),query(rd[t],mid+1,r,L,R));
}
//outside nxt顺序
int rt[N*20],lc[N*20],rc[N*20],tot=0;
void insert(int &t,int last,int l,int r,int key,int pos,int vl){
    t=++tot;
    insert(rt[t],rt[last],1,n,pos,vl);
    if(l==r)return;
    lc[t]=lc[last];
    rc[t]=rc[last];
    int mid=(l+r)>>1;
    if(key<=mid)insert(lc[t],lc[last],l,mid,key,pos,vl);
    else insert(rc[t],rc[last],mid+1,r,key,pos,vl);
}
int query(int t,int l,int r,int L,int R,int ql,int qr){
    if(!t)return 0;
    if(L<=l&&r<=R)return query(rt[t],1,n,ql,qr);
    int mid=(l+r)>>1;
    if(R<=mid)return query(lc[t],l,mid,L,R,ql,qr);
    if(L>mid)return query(rc[t],mid+1,r,L,R,ql,qr);
    return max(query(lc[t],l,mid,L,R,ql,qr),query(rc[t],mid+1,r,L,R,ql,qr));
}
int outrt[N];
int pre[N],nxt[N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)p[i].pos=i,scanf("%d",&p[i].val);
    for(int i=1;i<=n;i++)pre[i]=0,nxt[i]=n+1;
    for(int i=1;i<=n;i++)p[i].pre=pre[p[i].val],pre[p[i].val]=i;
    for(int i=n;i>=1;i--)p[i].nxt=nxt[p[i].val],nxt[p[i].val]=i;
    //for(int i=1;i<=1000;i++)cout<<i<<" "<<p[i].pre<<" "<<p[i].nxt<<endl;
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;i++)//持久化pre的顺序
        insert(outrt[i],outrt[i-1],1,n+1,p[i].nxt,p[i].pos,p[i].val);
    for(int i=1;i<=m;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        l=(l+lastans)%n+1;
        r=(r+lastans)%n+1;
        if(l>r)swap(l,r);
        int ql=1,qr=n,tmp=0;
        while(ql<=qr){
            int mid=(ql+qr)>>1;
            if(p[mid].pre<l)ql=mid+1,tmp=mid;
            else qr=mid-1;
        }
        printf("%d\n",lastans=query(outrt[tmp],1,n+1,r+1,n+1,l,r));
    }
    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_KDTree

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

137

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

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

weixin_34259232的博客 462

bzoj 3489: A simple rmq problem

传送门. 题解: 本来应该用数据结构大力解决这题,无非就是对于每一个点求一个next、last分别表示上一个和下一个和它相同的,然后变成了三维偏序问题。 然后随便想了个分块,考虑不仅对序列分块,还对值域分块。 设si,j,ks_{i,j,k}si,j,k​表示序列中的第i块到第j块在值域第k块的出现次数为1的个数。 再维护个前缀和pi,jp_{i,j}pi,j​表示序列前i块在j的个数。 然后就可...

Cold_Chair的博客 322

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(KD树代替可持久化树套树

题面:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解 可持久化树套树太难写了,直接用KD树水过去。。。 对于每个点,找到前一个跟它的值相同的点pre,与后一个跟它的值相同的点nxt 那么点 i 能够更新答案的充要条件就是:l<=i<=r ,prei<l , n...

crosnken的博客 349

BZOJ 3489 A simple rmq problem 可持久化树套树

题目大意:给定一个序列,多次询问某一区间中出现且仅出现一次的最大的数 令第i个数左侧第一个与这个数相同的数为last[i] 右侧第一个与这个相同的数为next[i] 那么一个数a[i]在区间内出现一次当且仅当last[i]r&&l 于是我们将元素按照last[i]排序并构建可持久化线段树 令pos为满足last[i] 每次查询我要查询的是第pos个版本的线段树内所有next[i]>r的数中

世界 4131

BZOJ3489】 A simple rmq problem

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

diaopinshuo4087的博客 225

BZOJ 3489: A simple rmq problem (KD-tree做法)

KD树水过这道可持久化树套树…其实就是个三维偏序 题解戳这里 CODE #include <bits/stdc++.h> using namespace std; #define ls (t[o].ch[0]) #define rs (t[o].ch[1]) const int MAXN = 100005; const int inf = 1e9; inline void read(i...

Ark 400

BZOJ3489:A simple rmq problem

浅谈主席树:https://www.cnblogs.com/AKMer/p/9956734.html 题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题目意思就是在线求\(pos\)在\([l,r]\),\(lst\)在\([0,l-1]\),\(nxt\)在\([r+1,n+1]\)的点中权值最大的。其中\(pos...

aga28832的博客 150

[BZOJ 3489] A simple rmq problem可持久化树套树

题目链接:BZOJ - 3489 题目分析 “因为是OJ上的题,就简单点好了。”——出题人 真的..好..简单... 首先,我们求出每个数的前一个与它相同的数的位置,即 prev[i] ,如果前面没有相同的数,prev[i] = 0。 再求出每个数的后一个与它相同的数的位置,即 next[i], 如果后面没有相同的数,next[i] = n + 1。 这样,对于 l > p...

weixin_30343157的博客 87

BZOJ 3489 A simple rmq problem

可持久化树套树发现这是一个多维偏序,可持久化树套树即可。#include<cstdio> #include<algorithm> #define N 100005 using namespace std; namespace runzhe2000 { int read() { int r = 0; char c = getchar(); for(;

无尽 419

bzoj 3489: A simple rmq problem 可持久化线段树套可持久化线段树

题意给出n个数,每个数为[1,n],有m个询问,每次询问[l,r]内最大的只出现过一次的数。强制在线。 n<=100000,m<=200000分析一开始还以为是分块,但想了想发现不可做,然后想了一下主席树什么的也还是不会做,就只好去看题解了。然后发现有好多神奇的做法,像KDtree,树套树,树套堆等,但我到现在还不会怎么用树套堆来做。先求出每一个位置的next和last分别表示前面第一个和它的数一

beginend 555

BZOJ3489 A simple rmq problem K-D Tree

传送门 什么可持久化树套树才不会写呢,K-D Tree大法吼啊 对于第\(i\)个数,设其前面最后的与它值相同的位置为\(pre_i\),其后面最前的与它值相同的位置为\(aft_i\),那么对于一个询问\((l,r)\)和一个位置\(i\),需要同时满足\(pre_i < l \leq i \leq r < aft_i\)时,第\(i\)个位置的值才能产生贡献。 将\((pre_i...

weixin_30369087的博客 99

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

bzoj3489: A simple rmq problem

分块

OZY的博客 553
上一篇: BZOJ3786 星系探索 【Splay维护dfs序】*
下一篇: 二维计算几何学习和理解(1)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值