• ACM学习历程——HDU3333 Turing Tree(线段树 && 离线操作)


    Problem Description
    After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick thing happens again...

    Now given a sequence of N numbers A1, A2, ..., AN and a number of Queries(i, j) (1≤i≤j≤N). For each Query(i, j), you are to caculate the sum of distinct values in the subsequence Ai, Ai+1, ..., Aj.
     
    Input
    The first line is an integer T (1 ≤ T ≤ 10), indecating the number of testcases below.
    For each case, the input format will be like this:
    * Line 1: N (1 ≤ N ≤ 30,000).
    * Line 2: N integers A1, A2, ..., AN (0 ≤ Ai ≤ 1,000,000,000).
    * Line 3: Q (1 ≤ Q ≤ 100,000), the number of Queries.
    * Next Q lines: each line contains 2 integers i, j representing a Query (1 ≤ i ≤ j ≤ N).
     
    Output
    For each Query, print the sum of distinct values of the specified subsequence in one line.
     
    Sample Input
    2
    3
    1 1 4
    2
    1 2
    2 3
    5
    1 1 2 1 3
    3
    1 5
    2 4
    3 5
     
    Sample Output
    1
    5
    6
    3
    6

    这个题要求区间内不同值的和,一开始没有任何思路,看了题解,原来需要对查询进行离线操作。

    因为需要求区间内互异值的和,对于一个固定的区间的话,自然只需要对于相同的值只留一个,其他置零即可。

    但是对于动态的查询区间,保留的那个值的位置相对关键。

    通过对查询的区间进行排序可以讲区间有序的排列(以区间的右端点递增排序)。

    因为这样的话,对于这个数列,从第一个逐个插入,那么区间是[1, 1]->[1, 2]->[1, 3]……这样生成的,如果我们对于a[i],把之前出现过的a[i]都置零,这样此时对于已生成的区间[1, i],我们查询区间和[k, i]的时候(因为区间是按照右端点有序查询的),必然对于任意值p,都是先包含离i最近的那个p,才会包含前面的p,而前面的p已经被置零,故不会加入计算。而离i最近的p又会加入计算,不会影响结果。

    所以这样边生成区间[1, i],边对于[k, i]区间查询。对于之前出现过的a[i]置零,便可以达到查询效果。当然最好输出的结果是按照题目要求的查询顺序输出的,这里采用了保存在sum数组中。

    不过这里还有一点就是,如何对于之前的a[i]置零,此处采用了map,map里保存了最右端的a[i]的脚标,这样不断更新即可。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #define LL long long
    
    using namespace std;
    
    //线段树
    //区间每点增值,求区间和
    const int maxn = 30005;
    struct node
    {
        int lt, rt;
        LL val;
    }tree[4*maxn];
    
    //向上更新
    void PushUp(int id)
    {
        tree[id].val = tree[id<<1].val + tree[id<<1|1].val;
    }
    
    //建立线段树
    void Build(int lt, int rt, int id)
    {
        tree[id].lt = lt;
        tree[id].rt = rt;
        tree[id].val = 0;//每段的初值,根据题目要求
        if (lt == rt)
        {
            //tree[id].val = 1;
            return;
        }
        int mid = (lt + rt) >> 1;
        Build(lt, mid, id<<1);
        Build(mid+1, rt, id<<1|1);
        //PushUp(id);
    }
    
    //更改区间内某个点的值
    void Change(int lt, int rt, int id, int to)
    {
        if (lt <= tree[id].lt && rt >= tree[id].rt)
        {
            tree[id].val = to;
            return;
        }
        int mid = (tree[id].lt + tree[id].rt) >> 1;
        if (lt <= mid)
            Change(lt, rt, id<<1, to);
        if (rt > mid)
            Change(lt, rt, id<<1|1, to);
        PushUp(id);
    }
    
    //查询某段区间内的he
    LL Query(int lt, int rt, int id)
    {
        if (lt <= tree[id].lt && rt >= tree[id].rt)
            return tree[id].val;
        int mid = (tree[id].lt + tree[id].rt) >> 1;
        LL ans = 0;
        if (lt <= mid)
            ans += Query(lt, rt, id<<1);
        if (rt > mid)
            ans += Query(lt, rt, id<<1|1);
        return ans;
    }
    
    struct qq
    {
        int from, to;
        int id;
    }q[100005];
    
    bool cmp(qq a, qq b)
    {
        return a.to < b.to;
    }
    
    int a[30005], n, m;
    LL sum[100005];
    
    void Work()
    {
        Build(1, n, 1);
        map<int, int> s;
        int t, now = 0;
        for (int i = 1; i <= n; ++i)
        {
            t = s[a[i]];
            if (t == 0)
            {
                Change(i, i, 1, a[i]);
                s[a[i]] = i;
            }
            else
            {
                Change(t, t, 1, 0);
                Change(i, i, 1, a[i]);
                s[a[i]] = i;
            }
            for (;now < m && q[now].to == i; now++)
            {
                sum[q[now].id] = Query(q[now].from, q[now].to, 1);
            }
        }
    }
    
    void Output()
    {
        for (int i = 0; i < m; ++i)
            printf("%I64d
    ", sum[i]);
    }
    
    int main()
    {
        //freopen("test.in", "r", stdin);
        int T;
        scanf("%d", &T);
        for (int times = 0; times < T; ++times)
        {
            scanf("%d", &n);
            for (int i = 1; i <= n; ++i)
                scanf("%d", &a[i]);
            scanf("%d", &m);
            for (int i = 0; i < m; ++i)
            {
                scanf("%d%d", &q[i].from, &q[i].to);
                q[i].id = i;
            }
            sort(q, q+m, cmp);
            Work();
            Output();
        }
        return 0;
    }
    
  • 相关阅读:
    Python-温度的转换
    这些Servlet知识你一定要知道,金九银十大厂面试官都爱问
    【建议收藏】一份阿里大牛花了三天整理出来的XML学习笔记,写的非常详细
    一年六个月十八天,从外包到字节跳动客户端提前批,没想到我也能够逆袭
    面试官:小伙子,你能给我说一下HashMap的实现原理吗?
    盘点一下面试官最爱问的泛型和包装类,建议反复观看,真的写的非常详细
    深度分析:面试阿里,字节跳动,美团90%被问到的List集合,看完还不懂算我输
    2020阿里Java面试题目大汇总,看看你离阿里还有多远,附答案!
    不会吧,你连Java 多线程线程安全都还没搞明白,难怪你面试总不过
    java开发两年,连Spring中bean的装配都不知道?你怎么涨薪啊
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/4471744.html
Copyright © 2020-2023  润新知