HDU 5919 Sequence II

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5919

------------------------------------------------------------------------------------

现场赛的时候由于不会主席树就用分块卡过去了

现在学习了主席树后感觉这题也是很有意思的

首先 这题要把问题转化为对于一段区间 求一个左端点相同的$($如果有多个取右端点最靠左的$)$子区间

这个子区间的不同数的个数等于原区间不同数的个数除二向上取整

 

求区间不同数个数是主席树模板题之一 建议先做完下面这题再来看

http://www.cnblogs.com/sagitta/p/5937253.html

对于这题 由于不是求一个给定区间不同数的个数 看起来似乎还需要对子区间右端点进行二分才行

如果真的按照这种思路就是$O(NlogN + Qlog^{2}N)$了 虽然此题也不卡这种解法

但是考虑到有不少线段树$/ST$表$/$倍增 再加一个二分的题目实际操作中并不需要在外层套一个二分

因此这里我们也可以试着看看能否直接利用线段树维护的区间上的信息来免去二分

 

再对问题进行观察 我们发现要求的子区间与原区间的左端点是一样的 即子区间左端点是固定的

因此我们可以考虑在主席树上维护后缀区间而不是前缀区间

维护后缀区间的话 我们只需在主席树上每次对左右区间进行判断到底往那个区间走就行$($逐步确定右端点位置$)$

这样就是$O(NlogN + QlogN)$的了

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int N = 2e5 + 10, T = N * 40;
  4 int sum[T], ch[T][2], root[N << 1], ma[N], last[N];
  5 int a[N];
  6 int croot, cnt, n, q;
  7 int build(int x, int L, int R)
  8 {
  9     if(L == R)
 10     {
 11         sum[x] = 0;
 12         return x;
 13     }
 14     int mid = (L + R) >> 1;
 15     ch[x][0] = build(++cnt, L, mid);
 16     ch[x][1] = build(++cnt, mid + 1, R);
 17     sum[x] = sum[ch[x][0]] + sum[ch[x][1]];
 18     return x;
 19 }
 20 int update(int x, int L, int R, int y, int delta, int pre)
 21 {
 22     if(L == R)
 23     {
 24         sum[x] = sum[pre] + delta;
 25         return x;
 26     }
 27     int mid = (L + R) >> 1;
 28     if(y <= mid)
 29     {
 30         ch[x][0] = update(++cnt, L, mid, y, delta, ch[pre][0]);
 31         ch[x][1] = ch[pre][1];
 32     }
 33     else
 34     {
 35         ch[x][0] = ch[pre][0];
 36         ch[x][1] = update(++cnt, mid + 1, R, y, delta, ch[pre][1]);
 37     }
 38     sum[x] = sum[ch[x][0]] + sum[ch[x][1]];
 39     return x;
 40 }
 41 int query(int x, int L, int R, int r)
 42 {
 43     if(R <= r)
 44         return sum[x];
 45     int mid = (L + R) >> 1;
 46     if(r <= mid)
 47         return query(ch[x][0], L, mid, r);
 48     return sum[ch[x][0]] + query(ch[x][1], mid + 1, R, r);
 49 }
 50 int findkth(int x, int L, int R, int k)
 51 {
 52     if(L == R)
 53         return L;
 54     int mid = (L + R) >> 1;
 55     if(k <= sum[ch[x][0]])
 56         return findkth(ch[x][0], L, mid, k);
 57     else
 58         return findkth(ch[x][1], mid + 1, R, k - sum[ch[x][0]]);
 59 }
 60 int t;
 61 int main()
 62 {
 63     scanf("%d", &t);
 64     for(int ca = 1; ca <= t; ++ca)
 65     {
 66         croot = cnt = 0;
 67         memset(last, 0, sizeof last);
 68         scanf("%d%d", &n, &q);
 69         root[++croot] = ++cnt;
 70         ma[0] = 1;
 71         build(cnt, 1, n);
 72         for(int i = 1; i <= n; ++i)
 73             scanf("%d", &a[i]);
 74         for(int i = n; i; --i)
 75         {
 76             ++croot;
 77             root[croot] = update(++cnt, 1, n, i, 1, root[croot - 1]);
 78             if(last[a[i]])
 79             {
 80                 ++croot;
 81                 root[croot] = update(++cnt, 1, n, last[a[i]], -1, root[croot - 1]);
 82             }
 83             last[a[i]] = i;
 84             ma[i] = root[croot];
 85         }
 86         printf("Case #%d:", ca);
 87         int ans = 0, l, r, x, y;
 88         while(q--)
 89         {
 90             scanf("%d%d", &x, &y);
 91             l = min((x + ans) % n + 1, (y + ans) % n + 1);
 92             r = max((x + ans) % n + 1, (y + ans) % n + 1);
 93             int cnt = query(ma[l], 1, n, r);
 94             ans = findkth(ma[l], 1, n, (cnt + 1) / 2);
 95             printf(" %d", ans);
 96         }
 97         puts("");
 98     }
 99     return 0;
100 }

 

posted @ 2016-10-08 14:52  sagitta  阅读(436)  评论(0编辑  收藏  举报