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;
}
posted @ 2020-05-26 13:04  wrjlinkkkkkk  阅读(142)  评论(0编辑  收藏  举报