区间不同值数量的查询

题目链接:https://www.luogu.com.cn/problem/P1972


这道题要问各区间中有多少个不同的值,由于该题中a_i的数据规模较小,不用离散化(数据规模更大时需要离散化后按该方法做). 我 们此时需要找到一种方法让每个值对于我们查询的贡献只有1,怎么做呢?

考虑这样一个序列:[1,3,4,2,1,3],如果按照简单的桶p_i来记录i出现的次数,我们即要统计i使得p_i\neq 0的数量,一次操作复杂度为O(n)显然效率低下,那我们尝试这样一个记录方法: 另开一个数组让其他位置为0,每个值最后一次出现的位置的值为1,如上述序列对应的[0,0,1,1,1,1],求出前缀和即为所求.

而对应到原题中,我们不妨将提问的区间以右端点从小到大的顺序依次考虑,设置一个指针cnt表示我们考虑到最右侧的范围. 依次移动指针到每个提问区间的右端点,且每经过一个元素,检验其是否出现过,若没出现过,记录cnt为其最后出现的位置并将上述提到的数组的cnt位置的值改为1;若出现过,则需先将数组在其上次出现的位置的值改成0.

该过程用树状数组维护即可.


代码实现:

#include<bits/stdc++.h>
using namespace std;
int const N=1e6+10;
struct qes{
    int l,r,id,ans;
}e[N];
inline bool cmp1(qes a,qes b){
    if(a.r!=b.r)return a.r<b.r;
    return a.l<b.l;
}
inline bool cmp2(qes a,qes b){
    return a.id<b.id;
}
int t[N],last[N],n,a[N];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int loc,int val){
    for(int i=loc;i<=n;i+=lowbit(i)){
        t[i]+=val;
    }
}
inline int query(int loc){
    int res=0;
    for(int i=loc;i>0;i-=lowbit(i)){
        res+=t[i];
    }
    return res;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int m;cin>>m;
    for(int i=1;i<=m;i++){
        cin>>e[i].l>>e[i].r;
        e[i].id=i;
    }
    sort(e+1,e+m+1,cmp1);
    int cnt=1;
    for(int i=1;i<=m;i++){
        for(;cnt<=e[i].r;cnt++){
            if(last[a[cnt]]){
                update(last[a[cnt]],-1);
            }
            update(cnt,1);
            last[a[cnt]]=cnt;
        }
        e[i].ans=query(e[i].r)-query(e[i].l-1);
    }
    sort(e+1,e+m+1,cmp2);
    for(int i=1;i<=m;i++){
        cout<<e[i].ans<<"\n";
    }
    return 0;
}

线段树查询区间0的数量可以通过以下步骤实现: 1. 定义线段树节点的结构体,包含区间左右端点、区间0的数量等信息。 2. 构建线段树,对于每个节点,如果区间内所有元素均为0,则将该节点的区间0的数量标记为区间长度;否则将区间0的数量记为0。 3. 实现查询区间0的数量的函数。对于每个查询区间,递归地遍历线段树,分为以下三种情况: a. 如果查询区间与当前节点区间没有交集,则返回0; b. 如果查询区间包含当前节点区间,则返回当前节点的区间0的数量; c. 否则,将查询区间拆分成左右两个子区间,继续递归查询,并将两个子区间的结果相加返回。 4. 在需要修改线段树节点的时,更新节点的区间0的数量。 下面是一个Python的示例代码: ```python class SegmentTreeNode: def __init__(self, start, end): self.start = start self.end = end self.zeros = 0 self.left = None self.right = None class SegmentTree: def __init__(self, nums): self.root = self.build_tree(nums, 0, len(nums)-1) def build_tree(self, nums, start, end): if start > end: return None node = SegmentTreeNode(start, end) if start == end: node.zeros = 1 if nums[start] == 0 else 0 else: mid = (start + end) // 2 node.left = self.build_tree(nums, start, mid) node.right = self.build_tree(nums, mid+1, end) node.zeros = node.left.zeros + node.right.zeros return node def query(self, start, end): return self.query_helper(self.root, start, end) def query_helper(self, node, start, end): if not node or start > node.end or end < node.start: return 0 if start <= node.start and end >= node.end: return node.zeros return self.query_helper(node.left, start, end) + self.query_helper(node.right, start, end) def update(self, idx, val): self.update_helper(self.root, idx, val) def update_helper(self, node, idx, val): if not node or idx < node.start or idx > node.end: return if node.start == node.end == idx: node.zeros = 1 if val == 0 else 0 return self.update_helper(node.left, idx, val) self.update_helper(node.right, idx, val) node.zeros = node.left.zeros + node.right.zeros ``` 在上面的示例代码中,`SegmentTreeNode`表示线段树的节点,`SegmentTree`表示线段树本身。`build_tree`函数用于构建线段树,`query`函数用于查询区间0的数量,`update`函数用于修改节点的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值