[SDOI2009][Bzoj1878] HH的项链 离线+树状数组
2017-10-27 20:49
288 查看
题目链接: Bzoj1878.
—————————————-
给定一个长度为n的序列an, 有m个询问, 每一次询问区间[l,r]内有多少个不同的数字.
(1≤n≤50000,1≤m≤200000,1≤ai≤1000000)
—————————————-
我们观察一下, 一次询问的答案是这段区间内数字的种类数, 但是有些数会重复出现许多次, 那么我们能不能让每一种数只提供一次贡献呢?
答案是可以的. 我们不妨先考虑暴力一点的做法, 对于每一个询问, 我们在区间里从左往右扫一遍, 假如当前这个数以前没有出现过, 就把这个位置的贡献加1, 假如以前出现过, 就把上一次出现的位置贡献减1, 再把当前位置的贡献加1, 这样就可以保证每一种数只会产生1的贡献. 最后统计这个区间内的贡献之和就行了.
乍一看这个做法似乎是毫无意义的, 因为你都已经从左往右扫了一遍了, 还那么麻烦的加1减1干什么? 直接用桶暴力去统计答案不是方便多了吗?
肤浅! 没看到上面是在单点修改区间查询嘛?
迂腐! 不知道有个套路叫做区间排序嘛?
咳咳, 我们按询问区间的右端点排序然后依次处理排序后的区间, 查询完第i个区间之后, 将第i+1个区间多出来的部分按照上面说的那样处理, 只不过由于是单点修改区间查询, 我们可以使用树状数组优化.
那么就这么愉快地分析完了这道模板题啦. 时间复杂度O((n+m)logn).
—————————————-
—————————————-
—————————————-
——wrote by miraclejzd.
—————————————-
概述
题目大意如下.给定一个长度为n的序列an, 有m个询问, 每一次询问区间[l,r]内有多少个不同的数字.
(1≤n≤50000,1≤m≤200000,1≤ai≤1000000)
—————————————-
分析
听说这是树状数组模板题? 那我还是太弱了..看了好久才发现可以用树状数组.我们观察一下, 一次询问的答案是这段区间内数字的种类数, 但是有些数会重复出现许多次, 那么我们能不能让每一种数只提供一次贡献呢?
答案是可以的. 我们不妨先考虑暴力一点的做法, 对于每一个询问, 我们在区间里从左往右扫一遍, 假如当前这个数以前没有出现过, 就把这个位置的贡献加1, 假如以前出现过, 就把上一次出现的位置贡献减1, 再把当前位置的贡献加1, 这样就可以保证每一种数只会产生1的贡献. 最后统计这个区间内的贡献之和就行了.
乍一看这个做法似乎是毫无意义的, 因为你都已经从左往右扫了一遍了, 还那么麻烦的加1减1干什么? 直接用桶暴力去统计答案不是方便多了吗?
肤浅! 没看到上面是在单点修改区间查询嘛?
迂腐! 不知道有个套路叫做区间排序嘛?
咳咳, 我们按询问区间的右端点排序然后依次处理排序后的区间, 查询完第i个区间之后, 将第i+1个区间多出来的部分按照上面说的那样处理, 只不过由于是单点修改区间查询, 我们可以使用树状数组优化.
那么就这么愉快地分析完了这道模板题啦. 时间复杂度O((n+m)logn).
—————————————-
代码
#include<bits/stdc++.h> #define For(i, j, k) for(int i = j; i <= (int)k; ++ i) using namespace std; const int maxn = 50000 + 5; const int maxm = 200000 + 5; int n, m, tot; int a[maxn], pre[maxn], c[maxn], Ans[maxm]; struct Que{ int l, r, id; bool operator < (Que o)const{ return r < o.r; } }q[maxm]; map<int,int> mp; inline int Read(){ int x = 0; char c = getchar(); for(; !isdigit(c); c = getchar()); for(; isdigit(c); c = getchar()) x = (x << 3 ) + (x << 1) + (c ^ 48); return x; } inline void add(int x, int v){ for(; x < maxn; x += x & (-x)) c[x] += v; } inline int sum(int x){ int back = 0; for(; x; x -= x & (-x)) back += c[x]; return back; } int main(){ file(); n = Read(); For(i, 1, n){ a[i] = Read(); mp[a[i]]; } m = Read(); For(i, 1, m){ q[i].id = i; q[i].l = Read(); q[i].r = Read(); } for(map<int,int>::iterator p = mp.begin(); p != mp.end(); ++ p) p->second = ++ tot;//这是一个优秀的离散化方法, 可以自己理解一下. For(i, 1, n) a[i] = mp[a[i]]; sort(q + 1, q + 1 + m); int now = 1, l, r; For(i, 1, m){ l = q[i].l; r = q[i].r; while(now <= r){ if(pre[a[now]]) add(pre[a[now]], -1); add(pre[a[now]] = now, 1); ++ now; } Ans[q[i].id] = sum(r) - sum(l - 1); } For(i, 1, m) printf("%d\n", Ans[i]); return 0; }
—————————————-
小结
这道模板题困扰了我还挺久的, 开始在想普通线段树, 后来发现维护不了, 于是想权值线段树, 结果发现理解错了题意… 后来冥思苦想半天才发现了我上面说的那个暴力方法, 用树状数组优化一下就可以了. 果然还是套路题做少了, 还要继续努力.—————————————-
——wrote by miraclejzd.
相关文章推荐
- BZOJ 1878 【SDOI2009】HH的项链(离线+树状数组)
- 【bzoj1878】[SDOI2009]HH的项链 树状数组
- 【SDOI2009】【BZOJ1878】HH的项链
- Bzoj 1878: [SDOI2009]HH的项链 莫队
- 【bzoj1878】【洛谷P1972】【SDOI2009】HH的项链
- 【BZOJ】【1878】【SDOI2009】HH的项链
- 【SDOI2009】【bzoj1878】HH的项链(离线+树状数组)
- bzoj 1878: [SDOI2009]HH的项链(莫队算法)
- BZOJ1878 [SDOI2009]HH的项链
- 【莫队 or 离线+树状数组】BZOJ1878(SDOI2009)[HH的项链]题解
- 【SDOI2009】bzoj1878 HH的项链【解法一】
- 【BZOJ1878】[SDOI2009]HH的项链 离线BIT
- [SDOI2009][bzoj1878] HH的项链 [莫队模板题]
- BZOJ1878 [SDOI2009]HH的项链
- [bzoj1878][SDOI2009][HH的项链] (莫队算法)
- BZOJ 1878 [SDOI2009]HH的项链 题解与分析
- bzoj千题计划181:bzoj1878: [SDOI2009]HH的项链
- bzoj1878: [SDOI2009]HH的项链(树状数组+离线处理)
- [BZOJ1878][SDOI2009]HH的项链
- bzoj 1878 [SDOI2009]HH的项链