您的位置:首页 > 其它

[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).

—————————————-

代码

#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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: