bzoj 3489 A simple rmq problem —— 主席树套线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489

题解:http://www.itdaan.com/blog/2017/11/24/9bc46b690756fe252e17fc3ca90aa01.html

在我挣扎一下午时 Narh 早就A了...

于是看看有何不同,发现 add  和 insert 中必须把 ls[x] = ls[y] , rs[x] = rs[y] 写在前面,而不能是修改 rs 则在那里单写一个 ls[x] = ls[y] 什么的,否则过不了样例...

然后 find 和 query 中一定要有一个 if(!x) return 0; ,否则秒 WA ...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
int const xn=1e5+5,xm=xn*20,xy=xm*20;
int n,m,cnt,rt[xn],ls[xm],rs[xm],cnt2,rt2[xm],ls2[xy],rs2[xy],lst[xn],mx[xy];
struct N{int pr,nxt,pos,val;}p[xn];
int rd()
{
    int ret=0,f=1; char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=0; ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘)ret=(ret<<3)+(ret<<1)+ch-‘0‘,ch=getchar();
    return f?ret:-ret;
}
bool cmp(N x,N y){return x.pr<y.pr;}
void add(int &x,int y,int l,int r,N t)
{
    x=++cnt2;
    ls2[x]=ls2[y]; rs2[x]=rs2[y];
    mx[x]=max(mx[y],t.val);
    if(l==r)return;
    if(t.pos<=mid)add(ls2[x],ls2[y],l,mid,t);
    else add(rs2[x],rs2[x],mid+1,r,t);
}
void insert(int &x,int y,int l,int r,N t)
{
    x=++cnt;
    ls[x]=ls[y]; rs[x]=rs[y];
    add(rt2[x],rt2[y],0,n+1,t);//!!!
    if(l==r)return;
    if(t.nxt<=mid)insert(ls[x],ls[y],l,mid,t);
    else insert(rs[x],rs[y],mid+1,r,t);
}
int find(int x,int l,int r,int L,int R)
{
    if(!x)return 0;//!!
    if(l>=L&&r<=R)return mx[x];
    int ret=0;
    if(mid>=L)ret=max(ret,find(ls2[x],l,mid,L,R));
    if(mid<R)ret=max(ret,find(rs2[x],mid+1,r,L,R));
    return ret;
}
int query(int x,int l,int r,int L,int R,int ql,int qr)
{
    if(!x)return 0;//!!
    if(l>=L&&r<=R)return find(rt2[x],0,n+1,ql,qr);
    int ret=0;
    if(mid>=L)ret=max(ret,query(ls[x],l,mid,L,R,ql,qr));
    if(mid<R)ret=max(ret,query(rs[x],mid+1,r,L,R,ql,qr));
    return ret;
}
int main()
{
    n=rd(); m=rd();
    for(int i=1,x;i<=n;i++)
    {
        x=rd();
        p[i].pos=i; p[i].val=x;
        p[i].pr=lst[x]; p[lst[x]].nxt=i;
        lst[x]=i;
    }
    for(int i=1;i<=n;i++)if(!p[i].nxt)p[i].nxt=n+1;
    sort(p+1,p+n+1,cmp);

    for(int i=0,t=1;i<=n+1;i++)
    {
        rt[i]=rt[i-1];
        while(p[t].pr==i&&t<=n)insert(rt[i],rt[i],0,n+1,p[t++]);
    }
    for(int i=1,x,y,l,r,lt=0;i<=m;i++)
    {
        x=rd(); y=rd();
        l=min((x+lt)%n+1,(y+lt)%n+1);
        r=max((x+lt)%n+1,(y+lt)%n+1);
        lt=query(rt[l-1],0,n+1,r+1,n+1,l,r);//
        printf("%d\n",lt);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Zinn/p/9719895.html

时间: 2024-10-03 23:56:21

bzoj 3489 A simple rmq problem —— 主席树套线段树的相关文章

bzoj 3489 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

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

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

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

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

bzoj 3489: A simple rmq problem

1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define M 200009 5 using namespace std; 6 struct A 7 { 8 int d[3],mx[3],mn[3],l,r,v,mx1; 9 }a[M]; 10 int n,b[M],v[M],N,ans,root,m; 11 bool cmp(A a1,A a2) 12 { 13 return a1.d

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

ZJOI 2017 树状数组(线段树套线段树)

题意 http://uoj.ac/problem/291 思路 不难发现,九条カレン醬所写的树状数组,在查询区间 \([1,r]\) 的时候,其实在查询后缀 \([r,n]\) :在查询 \([l,r](l\neq1)\) 的时候,则是在查询 \([l-1,r-1]\) .那么在查询 \([1,r]\) 的时候,只需要询问 \(r\) 的前后缀异或是否相等:在查询 \([l,r](l\neq 1)\) 的时候,只需要询问 \(a[l-1],a[r]\) 是否相等. 考虑 \(O(n^2)\) 的

【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad. void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); } 这里注意是mm*4...我该好好想想了..这是在dbg的时候找出来的问题.sad. 我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树

hdu-4819-线段树套线段树

http://acm.hdu.edu.cn/showproblem.php?pid=4819 给出一个N*N的矩阵,每次询问一个m*m的子矩阵里的floor((maxv+minv)/2)并把中间的元素修改为这个值. 线段树套线段树,第一层X表示对行建立的线段树,内层表示对Y也就是列建立的线段树. 分别对X和Y建立相应的函数来完成操作,当更新X树的节点时,再更新完当前X的节点的左右儿子之后,回头对X的这个节点对应的Y树进行更新,相当于X的左右儿子里的Y树来更新X的Y树,能理解这一点就很简单了. 1