• 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;
    }
    
  • 相关阅读:
    MySQL —— 程序连接时的驱动名称和URL
    这该称作什么效应?
    Java 基础 面向对象之关键字内部类代码块修饰符
    Java 基础 面向对象之构造方法和关键字
    Java 基础 接口和多态
    Java 基础 面向对象和抽象类
    Java 基础 引用数据类型 ArrayList集合
    Java 基础 方法
    Java 基础 引用数据类型 和 流程控制
    Mongodb 基础 复制集原理和搭建
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/4471744.html
走看看 - 开发者的网上家园