[BZOJ]3489 A simple rmq problem 主席树套树

来源:互联网 发布:在手机上怎么开淘宝店 编辑:程序博客网 时间:2024/06/05 14:49

3489: A simple rmq problem

Time Limit: 40 Sec Memory Limit: 600 MB
Submit: 2074 Solved: 708
[Submit][Status][Discuss]
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,但未重测

Source

by zhzqkkk

题解

  原来的编辑模式字体有毒改不过来啊... 这下粘题面就不能顺便粘走超链接. 只能用markdown了, 不过谁能告诉我markdown怎么行首空两格... Upd:现在会了.
  不会K-D Tree怎么办… 不过话说K-D Tree不会被卡吗?
  一开始在openjudge的cdqz小组里数据结构中看到此题, 不过那道题可以离线. 当时想了很久… 一开始觉得这道题非常的naive, 主席树搞一下就行了! 发现唯一出现实际上是一个三维关系.
  三维关系分别是如果a[i]要对L到R的询问构成贡献, 那么pre[a[i]] < L, nxt[a[i]] > R, L <= i <= R. 所以说就可以转化为三维空间, 用K-D Tree即可, 只是本蒟蒻不会…. 但是K-D Tree很难卡但是也应该是能被卡的吧.

  冥思苦想YY了一下树套树, 写了一发.
  但是从来没学过没做过树套树啊!!一上来还想了个主席树套树, 真的是作死. 当时想着时间空间都是nlog^2n的, 十万能过, 没有想过实现的困难就写了. 搞了半个下午… (突然想起之前没有学过带修改莫队和树上莫队直接去做糖果公园… 自己zuo啊… 那道200s的题有一次T了, 卡了评测两页gg).

  我们按pre排一下序, 这样sort可以消去一位.
  用主席树对pre值序列维护. root[i]保存了所有pre在i位置及之前的所有数字的信息(先不管是什么信息, 反正满足原序列里pre在i之前的就会被记录进去). 然后对于root[i], L-R区间代表的是整棵root[i]保存数字中满足nxt在L-R的数字的信息. 这样就满足pre和nxt的限制了.

  然后对于第三维, 我们就对于主席树的每个节点开一棵线段树, 这棵线段树的L-R保存的就是数字本身位置在L-R的信息, 这个信息就是这个数字的值. 这样就满足第三维了.
  这样我们对于L-R的查询, 在root[L-1]里查, 满足查询的数字的pre < L. 再进入到root[L-1]的R+1-n区间中, 这样就满足了nxt>R(主席树即外层的树是以nxt为下标的). 然后在这个区间里的线段所套的线段树查L-R的保存的信息即数字本身值得最大值. 这样三维就满足了.

  分析一下空间. 主席树动态开链, 构建主席树是每到一个位置i就会开一个log的链, 然后对于这条链上的每个节点开的线段树也动态开一条log链来保存i的信息, 这样就是log^2n的空间, 因为有n个位置, 所以是nlog^2n的空间.

  分析一下时间. 每次查询R+1-n时间包含log个节点, 每个节点还要进入所套的线段树再查L-R的最大值, 所以每次查询nlog^2n的.

  所以说可以过了~ 做出来还是成就满满. 但是openjudge的cdqz的那道练习过不去, 因为那道题n是300000而且空间只有512mb, bzoj有600mb… 于是乎MLE了.

  但是cdqz那道题可以离线啊…前辈过的都写得K-D Tree, 但是想到可以离线转化成二维平面上的矩形单点查询, 扫描线nlog完美解决... 日后搞算几练扫描线的时候再去A那道题吧, 说不定艹榜... Upd:发现还是要nlog^n?

  树套树代码不长, 常数有点大…(原来的代码风格的话不加读优可能就80多行)

#include<stdio.h>#include<algorithm>#define Acce register intusing namespace std;const int maxn = 1e5 + 5;const int maxm = 3e7 + 3e6;int n, m, l, r, ans, las, last[maxn];inline const int read(){    Acce x = 0;    char ch = getchar();    while (ch < '0' || ch > '9') ch = getchar();    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();    return x;}struct point{    int v, pre, nxt, id;    friend bool operator < (point x, point y)    { return x.pre < y.pre; }}a[maxn];struct node{    int cmax;    node *ls, *rs, *rt;}pool[maxm], *root[maxn], *null, *tail = pool;inline void init(){    null = ++ tail;    null -> cmax = 0;    null -> ls = null -> rs = null -> rt = null;}inline node* newnode(){    node* bt = ++ tail;    bt -> ls = bt -> rs = bt -> rt = null;    bt -> cmax = 0;    return bt;}void modify(node* bt, node* pre, int lf, int rg, int pos, int val){       bt -> cmax = max(max(bt -> cmax, pre -> cmax), val);    if (lf == rg) return;     int mid = (lf + rg) >> 1;    if (pos <= mid)    {        if(bt -> ls == null) bt -> ls = newnode();        if(bt -> rs == null) bt -> rs = pre -> rs;        modify(bt -> ls, pre -> ls, lf, mid, pos, val);    }   else    {        if(bt -> rs == null) bt -> rs = newnode();        if(bt -> ls == null) bt -> ls = pre -> ls;        modify(bt -> rs, pre -> rs, mid + 1, rg, pos, val);    }}void insert(node* bt, node* pre, int lf, int rg, point x){    if(bt -> rt == null) bt -> rt = newnode();    modify(bt -> rt, pre -> rt, 1, n, x.id, x.v);    if(lf == rg) return;    int mid = (lf + rg) >> 1;    if (x.nxt <= mid)    {        if (bt -> ls == null) bt -> ls = newnode();        if (bt -> rs == null) bt -> rs = pre -> rs;        insert(bt -> ls, pre -> ls, lf, mid, x);    }   else    {        if (bt -> rs == null) bt -> rs = newnode();        if (bt -> ls == null) bt -> ls = pre -> ls;        insert(bt -> rs, pre -> rs, mid + 1, rg, x);    }}int query_cmax(node *bt, int lf, int rg, int L, int R){    if (L <= lf && rg <= R) return bt -> cmax;    int rt1 = 0, rt2 = 0, mid = (lf + rg) >> 1;    if (L <= mid) rt1 = query_cmax(bt -> ls, lf, mid, L, R);    if (R > mid)  rt2 = query_cmax(bt -> rs, mid + 1, rg, L, R);    return max(rt1, rt2);}int query(node* bt, int lf, int rg, int L, int R){    if (L <= lf && rg <= R)        return query_cmax(bt -> rt, 1, n, l - 1, r - 1);    int rt1 = 0, rt2 = 0, mid = (lf + rg) >> 1;    if (L <= mid) rt1 = query(bt -> ls, lf, mid, L, R);    if (R > mid)  rt2 = query(bt -> rs, mid + 1, rg, L, R);    return max(rt1, rt2);}int main(){    init();    n = read(), m = read();    for (Acce i = 0; i <= n + 2; ++ i) root[i] = newnode(), root[i] -> rt = null;     for (Acce i = 1; i <= n; ++ i)     {        a[i].v = read();        a[i].pre = last[a[i].v] + 1;        last[a[i].v] = i;        a[i].id = i;    }    for (Acce i = 1; i <= n + 2; ++ i) last[i] = n + 1;    for (Acce i = n; i; -- i)    {        a[i].nxt = last[a[i].v] + 1;        last[a[i].v] = i;    }    sort(a + 1, a + n + 1);    for (Acce i = 1; i <= n; ++ i)    {        while(las < a[i].pre - 1) root[las + 1] = root[las], ++ las;        insert(root[a[i].pre], root[a[i].pre - 1], 1, n + 2, a[i]);         las = a[i].pre;    }    while(las <  n + 1) root[las + 1] = root[las], ++ las;    for (Acce i = 1; i <= m; ++ i)    {        l = read(), r = read();        l = (l + ans) % n + 1;        r = (r + ans) % n + 1;        if(l > r) swap(l, r);        ++ l, ++ r;        ans = query(root[l - 1], 1, n + 2, r + 1, n + 2);        printf("%d\n", ans);    }}
原创粉丝点击