zoukankan      html  css  js  c++  java
  • SPOJ DQUERY树状数组离线or主席树

    Time Limit: 227MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu

     Status

    Description

    Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.

    Input

    • Line 1: n (1 ≤ n ≤ 30000).
    • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
    • Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
    • In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).

    Output

    • For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.

    Example

    Input
    5
    1 1 2 1 3
    3
    1 5
    2 4
    3 5
    
    Output
    3
    2
    3 


    树状数组的方法:

    /*
    SPOJ DQUERY(hdu3333)线段树or树状数组离线
    查询区间内不同数的个数
    本来是学习主席树的,发现这方法不会也就写了下,感觉很机智
    先将所有查询按区间右端从小到大排序,如果一个数已经出现过就先把以前位置上的删
    掉然后在新的位置上插入,像这样在[l,r]中重复的就只计算了一次
    hhh-2016-02-18 14:47:11
    */
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <map>
    #include <queue>
    #include <vector>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    int tot;
    map<int , int >mp;
    const int maxn = 100010;
    int n;
    int a[maxn],ans[maxn*2];
    int s[maxn];
    
    struct node
    {
        int l,r,id;
    } qu[maxn*2];
    
    bool cmp(node a,node b)
    {
        return a.r < b.r;
    }
    
    int lowbit(int x)
    {
        return x&(-x);
    }
    
    
    void add(int x,int val)
    {
        for(int i = x ; i <= n; i+=lowbit(i))
            s[i] += val;
    }
    
    int query(int x)
    {
        int sum = 0;
        for(int i = x; i > 0; i -= lowbit(i))
            sum += s[i];
        return sum;
    }
    
    int main()
    {
        while(scanf("%d",&n) != EOF)
        {
            mp.clear();
            memset(s,0,sizeof(s));
            for(int i =1 ; i <= n; i++)
                scanf("%d",&a[i]);
    
            int q;
            scanf("%d",&q);
            for(int i = 1; i <= q; i++)
            {
                scanf("%d%d",&qu[i].l,&qu[i].r);
                qu[i].id = i;
            }
            sort(qu+1,qu+q+1,cmp);
            int t = 1;
            for(int i= 1;i <= q;i++)
            {
                for(;t <= qu[i].r;t++)
                {
                    if(mp[a[t]] != 0) add(mp[a[t]],-1);
                    mp[a[t]] = t;
                    add(t,1);
                }
                ans[qu[i].id] = query(qu[i].r) - query(qu[i].l-1);
            }
            for(int i = 1;i <= q;i++)
            {
                printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }
    
    
    
    主席树:  //参考:关于主席树的读书笔记
    
    将1-i用线段树处理,每个表示前i个数的情况。如果每棵线段都建完整的话肯定会mle,我们发现对于前缀[1,i]和前缀[1,i+1]的线段树,如果b[i+1]<=mid (b[i+1]表示a[i+1]离散后的标记) 那么线段树i和线段树i+1的左边是完全相同的,根本不需要在建,只需要用指针指一下就好
    
    //忘了在(=@__@=)哪里看的了
    
    换一种解释,如果我们要修改左子树,那么右子树上的与上一个线段树相比不会变化,只需要指一下就好
    
    
    /*
    主席树 SPOJ DQUERY
    查询区间有多少个不同的数。类似于之前树状数组离线的思路,在插入之前先进行判断
    如果已经有了,把以前的先删掉再进行插入
    hhh-2016-02-18 15:37:48
    */
    
    #include <functional>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    
    using namespace std;
    const int maxn = 100010;
    int tot;
    int n;
    int a[maxn],t[maxn];
    int T[maxn],lson[maxn*30],rson[maxn*30],c[maxn*30];
    
    int build(int l,int r)
    {
        int root = tot++;
        c[root] = 0;
        if(l != r)
        {
            int mid = (l+r)>>1;
            lson[root] = build(l,mid);
            rson[root] = build(mid+1,r);
        }
        return root;
    }
    
    
    //如果那里发生改变则兴建一个节点而非像平常修改那个节点的值
    int update(int root,int pos,int val)
    {
        int newroot = tot++;
        int tmp = newroot;
        c[newroot] = c[root] + val;
        int l = 1,r = n;
        while(l < r)
        {
            int mid = (l+r)>>1;
            if(pos <= mid)
            {
                lson[newroot] = tot++;
                rson[newroot] = rson[root];
                newroot = lson[newroot];
                root = lson[root];
                r = mid;
            }
            else
            {
                lson[newroot] = lson[root];
                rson[newroot] = tot++;
                newroot = rson[newroot];
                root = rson[root];
                l = mid+1;
            }
            c[newroot] = c[root] + val;
        }
        return tmp;
    }
    
    int query(int root,int pos)
    {
        int l = 1, r = n;
        int ans = 0;
        while(pos > l)
        {
            int mid = (l+r)>>1;
            if(pos <= mid)
            {
                ans += c[rson[root]];
                r = mid;
                root = lson[root];
            }
            else
            {
                l = mid+1;
                root = rson[root];
            }
        }
        return ans+c[root];
    }
    
    int main()
    {
        while(scanf("%d",&n) !=EOF)
        {
            tot = 0;
            map<int,int> mp;
            for(int i = 1; i <= n; i++)
                scanf("%d",&a[i]);
            T[0] = build(1,n);
            for(int i =1; i <= n; i++)
            {
                if(mp.find(a[i]) == mp.end())
                    T[i] = update(T[i-1],i,1);
                else
                {
                    int tt = update(T[i-1],mp[a[i]],-1);
                    T[i] = update(tt,i,1);
                }
                mp[a[i]] = i;
            }
    
            int q;
            scanf("%d",&q);
    
            while(q--)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                printf("%d
    ",query(T[r],l));
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    九度oj 题目1465:最简真分数
    九度oj 题目1083:特殊乘法 清华大学2010年机试题目
    九度oj 题目1084:整数拆分 清华大学2010年机试题目
    九度oj 题目1085:求root(N, k) 清华2010年机试题目
    九度oj 题目1460:Oil Deposit
    九度oj 题目1459:Prime ring problem
    九度oj 题目1458:汉诺塔III
    九度oj 题目1457:非常可乐
    题目1451:不容易系列之一
    移动端滚动不流畅,添加-webkit-overflow-scrolling属性 值为touch
  • 原文地址:https://www.cnblogs.com/Przz/p/5409628.html
Copyright © 2011-2022 走看看