BZOJ 2743 [HEOI2012]采花 (离线+树状数组)
题意
1e6的数组,1e6组询问,每次问你[l,r]内有多少个数出现超过了1次
空间128M
思路
主席树可以很轻松地解决这个问题,但是1e6,128M显然空间是不够的,所以我们要找别的方法
数组长度有1e6,根号级的算法也不能使用
我们考虑离线:
在左端点不变时,考虑用树状数组维护右端点在不同位置时的答案,那么当左端点改变时只需要改变一次树状数组即可
每次删去当前点a[i]的时候,那么下一个a[i]的位置到下下个a[i]的位置-1之间的点作为右端点的答案会-1,当下一个a[i]存在的时候才会有这个贡献
所以我们处理出初始答案后离线搞就行了
复杂度\(O(nlogn)\)
代码
int n,m;
int a[maxn];
int c[maxn];
void add(int x, int ad){
if(!x)return;
for(int i = x; i <= n; i+=lowbit(i))c[i]+=ad;
}
int sum(int x){
int ans =0 ;
for(int i = x; i; i-=lowbit(i))ans+=c[i];
return ans;
}
int k;
int vis[maxn],lst[maxn];
int nxt[maxn],ans[maxn];
vector<PI>v[maxn];
int main() {
scanf("%d %d %d", &n, &k,&m);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
lst[i]=vis[a[i]];
vis[a[i]]=i;
}
mem(vis,0);
for(int i = 1; i <= n; i++){
nxt[lst[i]]=i;
if(!vis[a[i]]){
if(lst[i]){vis[a[i]]=1;add(i,1);}
}
}
for(int i = 1; i <= m; i++){
int x,y;
scanf("%d %d", &x, &y);
v[x].pb(make_pair(y,i));
}
for(int i = 1; i <= n; i++){
sort(v[i].begin(),v[i].end());
for(int j = 0; j < (int)v[i].size(); j++){
int y = v[i][j].fst;
ans[v[i][j].sc]=sum(y);
}
int nx = nxt[i];
int nnx = nxt[nxt[i]];
if(nx){add(nx,-1);add(nnx,1);}
}
for(int i = 1; i <= m; i++){
printf("%d\n",ans[i]);
}
return 0;
}