SPOJ DQUUERY (在线主席树 | 离线树状数组)
2016-06-04 00:02
357 查看
DQUERY - D-query
#sorting #treeEnglish | Vietnamese |
..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct
elements in the subsequence ai, ai+1,
..., aj.
Input
Line 1: n (1 ≤ n ≤ 30000).Line 2: n numbers a1, a2,
..., an (1 ≤ ai ≤ 106).
Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).
Output
For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1,..., aj in a single line.
Example
Input 5 1 1 2 1 3 3 1 5 2 4 3 5 Output 3 2 3
Submit solution!
题意:询问区间内出现的数字有多少种.
离线和在线的做法都需要记录某一个数字出现的最远的位置.
离线树状数组:
询问按照右端点排序,然后每次从前一次询问的右端点扫描到当前询问的右端点,
每次将数字之前出现的位置的前缀和-1,新位置的前缀和+1,这个前缀和用树状
数组维护下就好了.询问[l,r]区间就是前缀和[1,r]-[1,l-1].
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <map> #include <set> #include <vector> using namespace std; #define maxn 211111 int a[maxn]; int n; vector <int> num; map <int , int> gg; struct node { int l, r, id; bool operator < (const node &a) const { return r < a.r; } }op[maxn]; int ans[maxn], pos[maxn]; void init () { int cnt = 0; sort (num.begin (), num.end ()); for (int i = 0; i < n; i++) { if (!i || num[i] != num[i-1]) { gg[num[i]] = ++cnt; } } for (int i = 1; i <= n; i++) { a[i] = gg[a[i]]; } } int c[maxn]; int lowbit (int x) { return x&(-x); } void add (int pos, int num) { for (int i = pos; i <= n; i += lowbit (i)) c[i] += num; } int sum (int pos) { int ans = 0; for (int i = pos; i > 0; i -= lowbit (i)) ans += c[i]; return ans; } int main () { while (scanf ("%d", &n) == 1) { gg.clear (); num.clear (); for (int i = 1; i <= n; i++) { scanf ("%d", &a[i]); num.push_back (a[i]); } init (); int q; scanf ("%d", &q); for (int i = 1; i <= q; i++) { scanf ("%d%d", &op[i].l, &op[i].r); op[i].id = i; } sort (op+1, op+1+q); memset (c, 0, sizeof c); memset (pos, -1, sizeof pos); int pre = 0; for (int i = 1; i <= q; i++) { for (; pre+1 <= op[i].r;) { pre++; add (pre, 1); if (pos[a[pre]] != -1) { add (pos[a[pre]], -1); } pos[a[pre]] = pre; } ans[op[i].id] = sum (op[i].r) - sum (op[i].l-1); } for (int i = 1; i <= q; i++) { printf ("%d\n", ans[i]); } } return 0; }
在线主席树:
记录每一个数字出现的最左边的位置.然后按照从右往左建可持久化线段树,
新增加一个数字就在前一个版本中的他出现的位置-1,新版本他出现的这个位置
+1.然后询问[l,r]的时候就只需要询问l版本线段树[l,r]的区间求和.
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <map> #include <set> #include <vector> using namespace std; #define maxn 211111 struct node { int l, r; int num; }tree[maxn<<6]; int T[maxn]; int cnt;//总结点数 int n, q, a[maxn], pre[maxn]; vector <int> num; map <int , int> gg; void init () { sort (num.begin (), num.end ()); int cnt = 0; for (int i = 0; i < num.size (); i++) { if (!i || num[i] != num[i-1]) { gg[num[i]] = ++cnt; } } for (int i = 1; i <= n; i++) a[i] = gg[a[i]]; } int build_tree (int l, int r) { int root = cnt++; tree[root].num = 0; if (l == r) return root; int mid = (l+r)>>1; tree[root].l = build_tree (l, mid); tree[root].r = build_tree (mid+1, r); return root; } int update (int root, int pos, int val) { int new_root = cnt++; int tmp = new_root; tree[new_root].num = tree[root].num+val; int l = 1, r = n; while (l < r) { int mid = (l+r)>>1; if (pos <= mid) { tree[new_root].l = cnt++; tree[new_root].r = tree[root].r; new_root = tree[new_root].l; root = tree[root].l; r = mid; } else { tree[new_root].l = tree[root].l; tree[new_root].r = cnt++; new_root = tree[new_root].r; root = tree[root].r; l = mid+1; } tree[new_root].num = tree[root].num + val; } return tmp; } int query (int root, int l, int r, int x, int y) { if (x == l && y == r) { return tree[root].num; } int mid = (l+r)>>1; if (mid >= y) { return query (tree[root].l, l, mid, x, y); } else if (mid < x) { return query (tree[root].r, mid+1, r, x, y); } else { return query (tree[root].l, l, mid, x, mid) + query (tree[root].r, mid+1, r, mid+1, y); } } int main () { //freopen ("in.txt", "r", stdin); while (scanf ("%d", &n) == 1) { num.clear (); gg.clear (); for (int i = 1; i <= n; i++) { scanf ("%d", &a[i]); num.push_back (a[i]); } init (); cnt = 0; memset (pre, -1, sizeof pre); T[n+1] = build_tree (1, n); for (int i = n; i >= 1; i--) { if (pre[a[i]] == -1) { T[i] = update (T[i+1], i, 1); } else { int tmp = update (T[i+1], pre[a[i]], -1); T[i] = update (tmp, i, 1); } pre[a[i]] = i; } int q; scanf ("%d", &q); while (q--) { int l, r; scanf ("%d%d", &l, &r); printf ("%d\n", query (T[l], 1, n, l, r)); } } return 0; }
相关文章推荐
- EasyUI布局 高度自适应
- testNg+ant+jenkins之ant配置build.xml
- EasyUI datebox 日期选择范围
- 左式堆 斜堆 buildHeap
- Android UI 之居中绘制文本内容的正确方法——实现自定义一个TextView
- iOS开发学习笔记-UIScrollView的用法
- GUI图形用户接口的基本使用
- windows平台时间函数性能比较QueryPerformanceCounter,GetTickCount,ftime,time,GetLocalTime,GetSystemTimeAsFileTim
- UESTC 491 Tricks in Bits (暴力回溯 + 剪枝)
- trickyPriorityQueue
- iOS开发——搜索框(UISearchController)
- pom.xml报错:web.xml is missing and <failOnMissingWebXml> is set to true
- UESTC 490 Swap Game (特殊的求解逆序对数)
- windows 安装 scrapy、pip、requests
- uibarbuttonitem颜色
- Android UI系列-----ImageView的scaleType属性
- NGUI--->制作血条UI
- Errors running builder "Integrated External Tool Builder" on project
- 1、CommonUtils.uuid()和CommonUtils.toBean(map, Person.class)
- 利用NtQuerySystemInformation函数遍历进程,遍历线程,获取线程挂起或运行状态