当前位置: 首页 > 工具软件 > DQuery > 使用案例 >

DQUERY - D-query——(主席树求区间不同数总个数)

史逸春
2023-12-01

题目链接:https://vjudge.net/problem/spoj-dquery
求区间不同数的总个数
n个数,数组a1...an,m次询问,区间al....ar中区间数的种数的总量
5
1 1 2 1 3
3
1 5
2 4
3 5

3
2

主席树:
    每颗线段树存每个点最后出现的位置,答案等于当前线段树的区间和
    
离线求法:
    对于求区间l~r的答案,等于1~r中的元素,每次最后一个点最后出现的位置+1
则sum[l,r]就是答案,用树状数组维护。
    把所有区间按r从小到大排序,按上述方法(每次维护最后一个点出现的位置)
遍历数组,每次遍历到区间的r,求一下当前区间的答案。

下述为主席树代码

#include<bits/stdc++.h>
#define M 30005
using namespace std;
int n,m,len_disc,tot;
int A[M],disc[M],root[M];
map<int,int>mp;
struct segment_tire
{
    int l,r,sum;
    segment_tire(int _l=0,int _r=0,int _sum=0):l(_l),r(_r),sum(_sum){};
}T[20*M];
void init_discate()
{
    for(int i=1;i<=n;i++)
        disc[i]=A[i];
    sort(disc+1,disc+1+n);
    len_disc=unique(disc+1,disc+1+n)-disc-1;
}
int discate(int x)
{
    return lower_bound(disc+1,disc+1+len_disc,x)-disc;
}
void update(int &rt,int old,int pos,int v,int l,int r)//从旧版本的pos位置更新v,存为新版本
{
    T[++tot]=T[old];
    T[tot].sum+=v;
    rt=tot;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(T[rt].l,T[old].l,pos,v,l,mid);
    else update(T[rt].r,T[old].r,pos,v,mid+1,r);
}
int query(int rt,int x,int y,int l,int r)//查询当前线段树区间l~r的和
{
    if(x<=l&&r<=y) return T[rt].sum;
    int mid=(l+r)>>1,ans=0;
    if(x<=mid) ans+=query(T[rt].l,x,y,l,mid);
    if(y>mid) ans+=query(T[rt].r,x,y,mid+1,r);
    return ans;
}
void init()
{
    tot=0;
    T[0]=segment_tire(0,0,0);
    mp.clear();
    root[0]=0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&A[i]);
    init_discate();
    init();
    for(int i=1;i<=n;i++)
    {
        //主席树中存的是最后一次出现位置的下标
        int dis=discate(A[i]);
        if(!mp[dis])//没出现过,当前位置+1
        {
            update(root[i],root[i-1],i,1,1,n);
            mp[dis]=i;
        }
        else//否则,更新最后一次出现的位置
        {
            update(root[i],root[i-1],mp[dis],-1,1,n);
            update(root[i],root[i],i,1,1,n);
            mp[dis]=i;
        }

    }
    int l,r;
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",query(root[r],l,r,1,n));//查询1~r版本的线段树l~r区间的和
    }
}

 

 类似资料: