标签:
这个题要求区间内不同值的和,一开始没有任何思路,看了题解,原来需要对查询进行离线操作。
因为需要求区间内互异值的和,对于一个固定的区间的话,自然只需要对于相同的值只留一个,其他置零即可。
但是对于动态的查询区间,保留的那个值的位置相对关键。
通过对查询的区间进行排序可以讲区间有序的排列(以区间的右端点递增排序)。
因为这样的话,对于这个数列,从第一个逐个插入,那么区间是[1, 1]->[1, 2]->[1, 3]……这样生成的,如果我们对于a[i],把之前出现过的a[i]都置零,这样此时对于已生成的区间[1, i],我们查询区间和[k, i]的时候(因为区间是按照右端点有序查询的),必然对于任意值p,都是先包含离i最近的那个p,才会包含前面的p,而前面的p已经被置零,故不会加入计算。而离i最近的p又会加入计算,不会影响结果。
所以这样边生成区间[1, i],边对于[k, i]区间查询。对于之前出现过的a[i]置零,便可以达到查询效果。当然最好输出的结果是按照题目要求的查询顺序输出的,这里采用了保存在sum数组中。
不过这里还有一点就是,如何对于之前的a[i]置零,此处采用了map,map里保存了最右端的a[i]的脚标,这样不断更新即可。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long using namespace std; //线段树 //区间每点增值,求区间和 const int maxn = 30005; struct node { int lt, rt; LL val; }tree[4*maxn]; //向上更新 void PushUp(int id) { tree[id].val = tree[id<<1].val + tree[id<<1|1].val; } //建立线段树 void Build(int lt, int rt, int id) { tree[id].lt = lt; tree[id].rt = rt; tree[id].val = 0;//每段的初值,根据题目要求 if (lt == rt) { //tree[id].val = 1; return; } int mid = (lt + rt) >> 1; Build(lt, mid, id<<1); Build(mid+1, rt, id<<1|1); //PushUp(id); } //更改区间内某个点的值 void Change(int lt, int rt, int id, int to) { if (lt <= tree[id].lt && rt >= tree[id].rt) { tree[id].val = to; return; } int mid = (tree[id].lt + tree[id].rt) >> 1; if (lt <= mid) Change(lt, rt, id<<1, to); if (rt > mid) Change(lt, rt, id<<1|1, to); PushUp(id); } //查询某段区间内的he LL Query(int lt, int rt, int id) { if (lt <= tree[id].lt && rt >= tree[id].rt) return tree[id].val; int mid = (tree[id].lt + tree[id].rt) >> 1; LL ans = 0; if (lt <= mid) ans += Query(lt, rt, id<<1); if (rt > mid) ans += Query(lt, rt, id<<1|1); return ans; } struct qq { int from, to; int id; }q[100005]; bool cmp(qq a, qq b) { return a.to < b.to; } int a[30005], n, m; LL sum[100005]; void Work() { Build(1, n, 1); map<int, int> s; int t, now = 0; for (int i = 1; i <= n; ++i) { t = s[a[i]]; if (t == 0) { Change(i, i, 1, a[i]); s[a[i]] = i; } else { Change(t, t, 1, 0); Change(i, i, 1, a[i]); s[a[i]] = i; } for (;now < m && q[now].to == i; now++) { sum[q[now].id] = Query(q[now].from, q[now].to, 1); } } } void Output() { for (int i = 0; i < m; ++i) printf("%I64d\n", sum[i]); } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 0; times < T; ++times) { scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); scanf("%d", &m); for (int i = 0; i < m; ++i) { scanf("%d%d", &q[i].from, &q[i].to); q[i].id = i; } sort(q, q+m, cmp); Work(); Output(); } return 0; }
ACM学习历程——HDU3333 Turing Tree(线段树 && 离线操作)
标签:
原文地址:http://www.cnblogs.com/andyqsmart/p/4471744.html