【bzoj3489】A simple rmq problem

Portal -->bzoj3489

Solution

  最近计划智力康复qwq(话说这题一年前刚刚开始写树套树的时候感觉好难啊qwq现在看其实还好也算是有进步的嘛!)

  比较重要的一步是,要将“在\([l,r]\)中只出现一次”这个条件转化成"\(nxt[x]>r\)&&\(pre[x]<l\)",其中\(nxt[x]\)表示下一个出现位置\(x\)的数的位置,\(pre[x]\)表示前一个

  然后我们就发现其实是有三个限制:

1、\(pre[x]<l\)

2、\(nxt[x]>r\)

3、最大

  那所以我们用两棵主席树套在一起就好了

?  外层的主席树按照\(pre[x]\)来顺序加点,树内按照\(nxt\)的顺序为关键字,然后对于每个节点(也就是区间啦),再开一棵主席树,以\(nxt\)为根的顺序,树内以\(val[x]\)为关键字(也就是权值线段树啦)

?  然后修改直接修改,查询的时候我们先二分一下外层主席树应该调用哪个\(rt\),也就是二分一个最大的\(pos\)满足\(pre[pos]<l\),然后\(query(rt[pos],l,r)\)即可

  想清楚了的话还是比较好写的

  有一些需要稍微注意一下的小细节

1、如果说对于那些后面已经没有与其相同的数的位置,为了方便我们将其\(nxt\)赋成\(n+1\),所以这里要注意主席树的区间应该是\([1,n+1]\)

2、查询的时候,要注意因为我们要求的是\(nxt[x]>r\),所以在查询中涉及到的范围应该是\(>\)而不是\(>=\)之类的,跟平时写线段树有那么一点点小区别稍微注意一下

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010,M=200010,SEG=N*20;
struct Data{
    int pre,nxt,id,val;
    friend bool operator < (Data x,Data y)
    {return x.pre<y.pre;}
}a[N];
int loc[N];
int n,m,ans;
namespace NodeSeg{/*{{{*/
    int ch[SEG*20][2],mx[SEG*20],rt[SEG];
    int n,tot;
    int newnode(int pre){
        ch[++tot][0]=ch[pre][0],ch[tot][1]=ch[pre][1]; mx[tot]=mx[pre];
        return tot;
    }
    void pushup(int x){mx[x]=max(mx[ch[x][0]],mx[ch[x][1]]);}
    void _update(int pre,int &x,int lx,int rx,Data &delta){
        x=newnode(pre);
        if (lx==rx){mx[x]=delta.val;return;}
        int mid=lx+rx>>1;
        if (delta.id<=mid) _update(ch[pre][0],ch[x][0],lx,mid,delta);
        else _update(ch[pre][1],ch[x][1],mid+1,rx,delta);
        pushup(x);
    }
    void update(int pre,int x,Data delta){_update(rt[pre],rt[x],1,n,delta);}
    int _query(int x,int l,int r,int lx,int rx){
        if (mx[x]==0) return 0;
        if (l<=lx&&rx<=r) return mx[x];
        int mid=lx+rx>>1,ret=0;
        if (l<=mid) ret=max(ret,_query(ch[x][0],l,r,lx,mid));
        if (r>mid) ret=max(ret,_query(ch[x][1],l,r,mid+1,rx));
        return ret;
    }
    int query(int x,int l,int r){return _query(rt[x],l,r,1,n);}
}/*}}}*/
namespace NextSeg{/*{{{*/
    int ch[SEG][2],rt[SEG];
    int n,tot;
    int newnode(int pre,Data delta){
        ch[++tot][0]=ch[pre][0]; ch[tot][1]=ch[pre][1];
        NodeSeg::update(pre,tot,delta);
        return tot;
    }
    void _update(int pre,int &x,int lx,int rx,Data delta){
        x=newnode(pre,delta);
        if (lx==rx) return;
        int mid=lx+rx>>1;
        if (delta.nxt<=mid) _update(ch[pre][0],ch[x][0],lx,mid,delta);
        else _update(ch[pre][1],ch[x][1],mid+1,rx,delta);
    }
    void update(int x,Data delta){_update(rt[x-1],rt[x],1,n,delta);}
    int _query(int x,int l,int r,int lx,int rx){
        if (r<lx&&rx<=n) return NodeSeg::query(x,l,r);
        int mid=lx+rx>>1,ret=0;
        if (r<mid) ret=max(ret,_query(ch[x][0],l,r,lx,mid));
        ret=max(ret,_query(ch[x][1],l,r,mid+1,rx));
        return ret;
    }
    int query(int x,int l,int r){return _query(rt[x],l,r,1,n);}
}/*}}}*/
int read(){
    int ret=0; char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret;
}

void prework(){
    for (int i=1;i<=n;++i) a[i].pre=0,a[i].nxt=n+1;
    for (int i=1;i<=n;++i){
        a[i].id=i;
        if (loc[a[i].val])
            a[loc[a[i].val]].nxt=i,a[i].pre=loc[a[i].val];
        loc[a[i].val]=i;
    }
    NodeSeg::n=n+1; NextSeg::n=n+1;
}

int get_pos(int x){
    int l=1,r=n,mid,ret=l;
    while (l<=r){
        mid=l+r>>1;
        if (x<=a[mid].pre) r=mid-1;
        else l=mid+1;
    }
    return l-1;
}

int main(){/*{{{*/
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    int x,y,x1,y1,pos;
    n=read(); m=read();
    for (int i=1;i<=n;++i) a[i].val=read();
    prework();
    sort(a+1,a+1+n);
    for (int i=1;i<=n;++i)
        NextSeg::update(i,a[i]);
    for (int i=1;i<=m;++i){
        x1=read(); y1=read();
        x=min((x1+ans)%n+1,(y1+ans)%n+1);
        y=max((x1+ans)%n+1,(y1+ans)%n+1);
        pos=get_pos(x);
        ans=NextSeg::query(pos,x,y);
        printf("%d\n",ans);
    }
}/*}}}*/

原文地址:https://www.cnblogs.com/yoyoball/p/9313850.html

时间: 2024-10-01 07:48:48

【bzoj3489】A simple rmq problem的相关文章

【BZOJ3489】A simple rmq problem kd-tree

[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行,每

【bzoj3489】 A simple rmq problem k-d树

由于某些原因,我先打了一个错误的树套树,后来打起了$k-d$.接着因不明原因在思路上被卡了很久,在今天中午蹲坑时恍然大悟...... 对于一个数字$a_i$,我们可以用一组三维坐标$(i,pre,nxt)$来表示,其中$i$表示该数字下标,$pre$表示在区间$[1,i)$中满足$a[j]=a[i]$的最大$j$,若不存在,则$pre=0$.$nxt$表示在区间$(i,n]$中满足$a[j]=a[i]$的最小$j$,若不存在,则$nxt=n+1$. 接着我们种一棵3-d树去存储这n个点.对于任意

【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],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路

【HDOJ】2451 Simple Addition Expression

递推,但是要注意细节.题目的意思,就是求s(x) = i+(i+1)+(i+2),i<n.该表达中计算过程中CA恒为0(包括中间值)的情况.根据所求可推得.1-10: 31-100: 3*41-1000: 3*4*41-10000: 3*4*4*41-10^n: 3*4^(n-1).并且需要注意,一旦发现某一位大于3,则应立即跳出累加的循环.比如,f(133) = 24,f(143) = 24.同时,单独讨论个位的情况.28行的break处理该种情况. 1 #include <cstdio&g

【hoj】 1017 Joseph&#39;s problem II

这个是约瑟夫的另一个变型,变为总共有2*k个人,先是K个好人后是k个坏人,要求前k次都要杀坏人,即在杀掉第一个好人之前就要把所有的坏人都杀光,所以需要我们求出满足这个条件的最小的m值: 由约瑟夫的递归模型可以发现,我们因为他的递归是从最后杀的人递归到原有的人数,所以我们可以吧顺序反过来,等价于最后杀掉k个坏人,再杀好人,这样在递归的时候就是先知道起始位置(先杀的人),这样就能迭代,由有好人时是否杀的是坏人来判定这个m是否适合,如果k次后杀到了第k个坏人则说明这个m是适合的 参考:http://w

【hoj】1016 Joseph&#39;s problem I

约瑟夫问题是一个很经典的问题,描述的是n的人围成一圈,每次数到第m个人就会被淘汰,之后在淘汰的人开始在数起第m个人,这样下去只带还剩下1个人为胜利者,这个题是约瑟夫问题的变形,它每次裁定的标准不再是一个恒定的m而是按照素数表中的第i次淘汰第i个人,所以我们需要求出素数表才能知道裁定的次序,也才能求出剩下的人的序号 首先,对于约瑟夫原本的问题是可以对每次淘汰使用逐个列举,将这n个人每个人都列举,没有出局的话就计1直到数到还没淘汰的第m个,但是这样下来对于n值很大的情况就会很耗时间,所以一定会有别的

【CF903G】Yet Another Maxflow Problem 线段树

[CF903G]Yet Another Maxflow Problem 题意:一张图分为两部分,左边有n个点A,右边有m个点B,所有Ai->Ai+1有边,所有Bi->Bi+1有边,某些Ai->Bj有边,每条边都有一定的容量. 先要求你支持两种操作: 1.修改某条Ai->Ai+1的边的容量2.询问从A1到Bm的最大流 n,m<=100000,流量<=10^9 题解:很有思维含量的题. 首先,我们把求最大流变成求最小割.容易发现,我们最多只会割一条Ai->Ai+1的边

【POJ】2480 Longge&#39;s problem(欧拉函数)

题目 传送门:QWQ 分析 题意就是求∑gcd(i, N) 1<=i <=N.. 显然$ gcd(i,n) = x $时,必然$x|n$. 所以我们枚举一下n的约数,对于每个约数x,显然$ gcd(i/x,n/x)=1$ 所以我们计算一下n/x的欧拉函数就ok了. 联赛前刷水题qwq 代码 // #include <bits/stdc++.h> #include <cstdio> #include <cmath> #include <algorithm

【kd-tree】bzoj3489 A simple rmq problem

Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足(pre[i]<ql and nex[i]>qr and i∈[ql,qr]) 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路不由自主又代入到搞[子树最大值]来更新答案……然而忘记了