SPOJ DQUERY 区间内不同数的个数 主席树

#include <iostream>
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 30010,MAXLOG = 20;

struct ChairTree
{
    int l,r;
    int ans;
}ct[MAXN*MAXLOG];
int ctRoot[MAXN];
int ctTop;

int num[MAXN],tnum[MAXN],pre[MAXN];

int CreatNewNode()
{
    ct[ctTop].l = -1,ct[ctTop].r = -1;
    ct[ctTop].ans = 0;
    return ctTop++;
}

void InitZeroLayer(int &root,int l,int r)
{
    root = CreatNewNode();

    if(l == r)
        return ;
    int mid = (l+r)>>1;
    InitZeroLayer(ct[root].l,l,mid);
    InitZeroLayer(ct[root].r,mid+1,r);
}

int BS(int L,int R,int x)
{
    int mid;

    while(L <= R)
    {
        mid = (L+R)>>1;
        if(tnum[mid] == x)
            return mid;
        if(tnum[mid] < x)
            L = mid+1;
        else
            R = mid-1;
    }
    return -1;
}

void PushUp(int root)
{
    ct[root].ans = ct[ct[root].l].ans + ct[ct[root].r].ans;
}

void Update(int pre,int &root,int L,int R,int goal,int info)
{
    if(root == -1 || root == pre)
    {
        root = CreatNewNode();
        ct[root].ans = ct[pre].ans;
    }
    if(L == R)
    {
        ct[root].ans += info;
        return ;
    }

    int mid = (L+R)>>1;

    if(goal <= mid)
    {
        if(ct[root].r == -1)
            ct[root].r = ct[pre].r;
        Update(ct[pre].l,ct[root].l,L,mid,goal,info);
    }
    else
    {
        if(ct[root].l == -1)
            ct[root].l = ct[pre].l;
        Update(ct[pre].r,ct[root].r,mid+1,R,goal,info);
    }

    PushUp(root);
}

void InitCt(int n)
{

    memset(pre,-1,sizeof(pre));
    sort(tnum+1,tnum+n+1);
    int site,tn,i;

    for(tn = 1,i = 2;i <= n; ++i)
        if(tnum[i] != tnum[tn])
            tnum[++tn] = tnum[i];

    memset(ctRoot,-1,sizeof(ctRoot));
    ctTop = 0;
    InitZeroLayer(ctRoot[0],1,n);

    for(i = 1;i <= n; ++i)
    {
        site = BS(1,tn,num[i]);
        if(pre[site] == -1)
            Update(ctRoot[i-1],ctRoot[i],1,n,i,1);
        else
        {
            Update(ctRoot[i-1],ctRoot[i],1,n,pre[site],-1);
            Update(ctRoot[i-1],ctRoot[i],1,n,i,1);
        }
        pre[site] = i;
    }
}

int Query(int root,int goal,int L,int R)
{
    if(goal == L)
        return ct[root].ans;

    int mid = (L+R)>>1;

    if(mid+1 <= goal)
        return Query(ct[root].r,goal,mid+1,R);
    return Query(ct[root].l,goal,L,mid) + ct[ct[root].r].ans;
}

int main()
{
    int i,l,r,q,n;

    while(scanf("%d",&n) != EOF)
    {
        for(i = 1;i <= n; ++i)
        {
            scanf("%d",&num[i]);
            tnum[i] = num[i];
        }

        InitCt(n);

        scanf("%d",&q);

        while(q--)
        {
            scanf("%d %d",&l,&r);

            printf("%d\n",Query(ctRoot[r],l,1,n));
        }
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值