3489: A simple rmq problem

3489: A simple rmq problem

Time Limit: 40 Sec   Memory Limit: 600 MB
Submit: 1761   Solved: 592
[ Submit][ Status][ Discuss]

Description

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

 

Input

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

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

再下面M行,每行两个整数xy

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

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

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

Lastans表示上一个询问的答案,一开始lastans0

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

[ Submit][ Status][ Discuss]

询问是在线的,不过数组是一开始就已经确定好的
对于数组中每个位置的数,预处理pre[i]为上一次出现这个数字的位置没有的话是0
同理,nex[i]为下一次出现这个数字的位置,若没有就是n+1
考虑位置i的数字Ai,那么它对所有左端点∈[pre[i]+1,i]右端点∈[i,nex[i]-1]的区间都有贡献
把左端点坐标对应到x轴,右端点坐标对应到y轴,那么有贡献的区域就是二维平面上的一个矩形
因此,预处理和查询都可以直接使用二维线段树来完成,复杂度是O(nlog^2n + qlog^2n)
交上去成功发现,,,TLE。。。。。
卡了一波常数,再交一遍结果发现,,,还是TLE。。。。。
要了数据一看,查询简直慢得要死。。。毕竟是几乎跑满的log^2呀。。。
于是很容易想到一个剪枝,如果当前搜下去能够找到的最优解不超过已经搜到的最优解,直接返回就行了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;
 
const int maxn = 1E5 + 10;
const int N = 300;
const int T = 4;
 
int n,m,cnt,LastAns,ans,A[maxn],last[maxn],L[maxn],R[maxn],rt[maxn*T],ma[maxn*T]
    ,lc[maxn*N],rc[maxn*N],Max[maxn*N],ma_son[maxn*N],Ans[maxn*2],ll[maxn*2],rr[maxn*2];
 
inline void query(int o,int l,int r,int pos)
{
    ans = max(ans,Max[o]);
    if (ma_son[o] <= ans) return; if (l == r) return;
    int mid = l + r >> 1;
    if (pos <= mid) query(lc[o],l,mid,pos);
    else query(rc[o],mid+1,r,pos);
}
 
inline void Query(int o,int l,int r,int ql,int qr)
{
    if (ma[o] <= ans) return; query(rt[o],1,n,qr);
    if (l == r) return; int mid = l + r >> 1;
    if (ql <= mid) Query(o<<1,l,mid,ql,qr);
    else Query(o<<1|1,mid+1,r,ql,qr);
}
 
inline void insert(int &o,int l,int r,int ql,int qr,int k)
{
    if (!o) o = ++cnt; ma_son[o] = max(ma_son[o],k);
    if (ql <= l && r <= qr) {Max[o] = max(Max[o],k); return;}
    int mid = l + r >> 1;
    if (ql <= mid) insert(lc[o],l,mid,ql,qr,k);
    if (qr > mid) insert(rc[o],mid+1,r,ql,qr,k);
}
 
inline void Insert(int o,int l,int r,int x,int y,int z,int k)
{
    ma[o] = max(ma[o],k);
    if (x <= l && r <= y) {insert(rt[o],1,n,y,z,k); return;}
    int mid = l + r >> 1;
    if (x <= mid) Insert(o<<1,l,mid,x,y,z,k);
    if (y > mid) Insert(o<<1|1,mid+1,r,x,y,z,k);
}
 
inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i <= n; i++) A[i] = getint();
    for (int i = 1; i <= n; i++)
        L[i] = last[A[i]],last[A[i]] = i;
    for (int i = 1; i <= n; i++) last[i] = n + 1;
    for (int i = n; i; i--)
        R[i] = last[A[i]],last[A[i]] = i;
    for (int i = 1; i <= n; i++)
        Insert(1,1,n,L[i] + 1,i,R[i] - 1,A[i]);
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    //cout << cnt << endl;
    for (int i = 1; i <= m; i++)
    {
        int l = getint(),r = getint();
        l = (l + LastAns) % n + 1;
        r = (r + LastAns) % n + 1; if (l > r) swap(l,r);
        //printf("%d\n",LastAns = Query(1,1,n,l,r));
        ans = 0; Query(1,1,n,l,r); printf("%d\n",LastAns = ans);
        //printf("%d : ",i); puts(LastAns == Ans[i] ? "Accepted" : "WrongAnswer");
    }
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值