SPOJ - DQUERY :D-query(主席树或离线)

本文介绍使用主席树在线解决SPOJ上的DQUERY问题,该问题要求查询序列中特定区间内的不同数值个数。文章详细阐述了解题思路,并通过样例解释如何利用主席树记录每个数最后一次出现的位置,从而实现高效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目链接:http://www.spoj.com/problems/DQUERY/en/


题目大意:

给一段序列,每次询问l , r之间有多少个不同的数。


解题思路:

 这道题以前做的时候用的是离线+线段树或者树状数组,最近学了主席树,就可以用主席树来在线解题了。而且貌似感觉自己用莫队解过这道题23333,反正解法很多,这里主要说主席树的解法,主席树的解法主要是记录每个数最右边的位置对答案的贡献。拿样例举例子,画个图应该很容易明白。

 


这个图呢就是对应样例画出来的对应1 1 2 1 3 的图,其实这里面每个1就是代表我这个位置对答案有1的贡献,而且只记录每个数字最后出现的位置,那么这样之后我们查询l r区间的时候 就直接在 root[r] l,n查询即可,

而且我感觉自己代码写的挺好看的2333

Ac代码:


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int maxn=1e6+10;
const int INF=1e9+7;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int n,m,cnt;
int a[N],root[N],last[maxn];    //last数组记录位置 因为数不大  不需要离散化
struct node
{
    int l,r,sum;
}t[N*40];
void update(int l,int r,int &x,int y,int val,int pos)   //主席树更新操作 val对应+1 -1
{
    t[++cnt]=t[y],t[cnt].sum+=val,x=cnt;
    if(l==r)
        return ;
    int m=(l+r)>>1;
    if(pos<=m)
        update(l,m,t[x].l,t[y].l,val,pos);
    if(pos>m)
        update(m+1,r,t[x].r,t[y].r,val,pos);
}
int query(int l,int r,int x, int L,int R)   //主席树简单的查询操作
{
    int ans=0;
    if(L<=l&&r<=R)
        return t[x].sum;
    int m=(l+r)>>1;
    if(L<=m)
        ans+=(query(l,m,t[x].l,L,R));
    if(R>m)
        ans+=(query(m+1,r,t[x].r,L,R));
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        cnt=0;
        memset(root,0,sizeof root);
        memset(last,-1,sizeof last);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            if(last[a[i]]==-1)
                update(1,n,root[i],root[i-1],1,i);  //如果这个数第一次出现 将它所在位置贡献+1
            else
            {
                int sk;
                update(1,n,sk,root[i-1],1,i);   //不是第一次出现 将上次出现的位置贡献消去
                update(1,n,root[i],sk,-1,last[a[i]]);   //当前位置贡献+1
            }
            last[a[i]]=i;
        }
        scanf("%d",&m);
        while(m--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(1,n,root[r],l,n));
        }
    }
    //system("pause");
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值