区间不同数的个数(可持久化线段树)

 题目:https://vjudge.net/problem/SPOJ-DQUERY

思路:维护每个数上一次出现的位置,若之前出现过,每次更新将上一次出现位置的树链复制即可,等价于将出现多次的数看作只出现一次。

#include <bits/stdc++.h>
using namespace std;
#define CLR(arr,val) memset(arr,val,sizeof(arr))
typedef long long LL;
const int N = 30010;

struct seg{int lson, rson, cnt;}T[N*20];
int root[N], tot, arr[N], last_pos[N];
vector<int>pos;
void init(){
    pos.clear();tot=0;
    CLR(root,0); CLR(last_pos,0);
    T[0].cnt=T[0].lson=T[0].rson=0;
}
void update(int &cur, int ori, int l, int r, int pos, int flag){
    cur = ++tot; T[cur] = T[ori]; T[cur].cnt += flag;
    if(l == r) return ;
    int mid = l+r >> 1;
    if(pos <= mid) update(T[cur].lson, T[ori].lson, l, mid, pos, flag);
    else update(T[cur].rson, T[ori].rson, mid+1, r, pos, flag);
}
int query(int S, int E, int l,int r, int x, int y){
    if(x<=l && y>=r) return T[E].cnt - T[S].cnt;
    else {
        int mid = l+r >> 1;
        if(y <= mid) return query(T[S].lson, T[E].lson, l, mid, x, y);
        else if(x > mid) return query(T[S].rson, T[E].rson, mid+1, r, x, y);
        else return query(T[S].lson,T[E].lson, l, mid, x, mid)+query(T[S].rson, T[E].rson, mid+1, r, mid+1, y);
    }
}
int main()
{
    int n,m,i,l,r;
    while (~scanf("%d",&n)) {
        init();
        for (i=1; i<=n; ++i) scanf("%d",&arr[i]), pos.push_back(arr[i]);
        sort(pos.begin(),pos.end()); pos.erase(unique(pos.begin(),pos.end()),pos.end());
        int temp_rt=0; scanf("%d",&m);
        for (i=1; i<=n; ++i) {
            arr[i]=lower_bound(pos.begin(),pos.end(),arr[i])-pos.begin()+1;
            if(!last_pos[arr[i]]) {
                update(root[i],root[i-1],1,n,i,1);
                last_pos[arr[i]]=i;
            } else {
                update(temp_rt,root[i-1],1,n,last_pos[arr[i]],-1);
                update(root[i],temp_rt,1,n,i,1);
                last_pos[arr[i]]=i;
            }
        }
        for (i=0; i<m; ++i) {
            scanf("%d%d",&l,&r);
            printf("%d\n",query(root[l-1],root[r],1,n,l,r));
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值