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


这题跟HDU3333差不多吧。

离线的做法很简单,不再说了

以前做过。



主席树的做法就比较暴力了。。


什么是主席树呢。。

其实是某种称号。

在该题中的体现是可持久化的线段树。

对于一个数

如果以前没出现过

就插入到主席树中

否则就删除以前那个。

再插入主席树。

注意,所有的更新和删除都是建立了新的节点来保持其历史状态的。。

对于T[i]我们存的是从1到i区间的不同的数出现了多少个。

然后这棵树是根据T[i - 1]来建立的。


代码如下。。第一次写主席树。 几乎是照着爱将的代码写的。

不过他是倒着来插入的,我是正向来的。 无非是一个以左端点为根查询。一个以询问的右端点为根查询,


 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#define INF 111111111
#define MAXN 33333
#define MAXM 600005
using namespace std;
int n, tot, q, a[MAXN];
int T[MAXM], lson[MAXM], rson[MAXM], val[MAXM];
int nxt[MAXN], b[MAXN];
int build(int l, int r)
{
    int rt = tot++;
    val[rt] = 0;
    int m = (l + r) >> 1;
    if(l != r)
    {
        lson[rt] = build(l, m);
        rson[rt] = build(m + 1, r);
    }
    return rt;
}
int update(int rt, int pos, int v)
{
    int newrt = tot++, tmp = newrt;
    int l = 1, r = n;
    val[newrt] = val[rt] + v;
    while(l < r)
    {
        int m = (l + r) >> 1;
        if(pos <= m)
        {
            lson[newrt] = tot++;
            rson[newrt] = rson[rt];
            newrt = lson[newrt];
            rt = lson[rt];
            r = m;
        }
        else
        {
            rson[newrt] = tot++;
            lson[newrt] = lson[rt];
            newrt = rson[newrt];
            rt = rson[rt];
            l = m + 1;
        }
        val[newrt] = val[rt] + v;
    }
    return tmp;
}

int query(int rt, int pos)
{
    int ret = 0;
    int l = 1, r = n;
    while(pos > l)
    {
        int m = (l + r) >> 1;
        if(pos <= m)
        {
            ret += val[rson[rt]];
            rt = lson[rt];
            r = m;
        }
        else
        {
            l = m + 1;
            rt = rson[rt];
        }
    }
    return ret + val[rt];
}
int main()
{
    while(scanf("%d", &n) != EOF)
    {
        tot = 0;
        memset(nxt, -1, sizeof(nxt));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            b[i - 1] = a[i];
        }
        sort(b, b + n);
        int cnt = unique(b, b + n) - b;
        T[0] = build(1, n);
        for(int i = 1; i <= n; i++)
        {
            int id = lower_bound(b, b + cnt, a[i]) - b;
            if(nxt[id] == -1)
                T[i] = update(T[i - 1], i, 1);
            else
            {
                int t = update(T[i - 1], nxt[id], -1);
                T[i] = update(t, i, 1);
            }
            nxt[id] = i;
        }
        scanf("%d", &q);
        while(q--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            printf("%d\n", query(T[r], l));
        }
    }
    return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值