HDU3874 线段树 + 离线处理

  题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3874 , 线段树(或树状数组) + 离线处理

  下午做了第一道离线处理的题目(HDU4417),多少有点感觉,顺便就把这道题也给做了。


  这道题就是要求某个区间内不重复数的和,自己在网上百度后参考别人的方法才AC的。参考来源:http://www.cnblogs.com/gj-Acit/p/3249827.html

  这道题还是需要先离线处理数据,具体方法:

  ① 先用线段树把树给建立起来;

  ② 先把所有要查询的区间存起来,按照区间的右边界的大小从小到大排序,注意保存下标;

    ③ 对于排序后的每次询问要这样处理:把上次询问区间的右边界到当前询问的右边界中的这些数字进行处理,如果这些数字在之前出现过,则把之前出现的这个数字给删掉(置0即可),然后单点更新线段树;然后查询的话用线段树进行区间求和即可。

  我参考的方法中是这样处理的,即用一个数组vis[]存储数字a[i]第一次出现的位置,后来扫描的时候如果发现 vis[a[i]] != i ,即说明该数字之前出现过。

  最后注意这里用int储存结果会爆掉的,所以要用__int64.

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define LL __int64
#define eps 1e-8
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int MOD = 10000007; 
const int maxn = 50000 + 5;
const int N = 1000000 + 5;
LL sum[maxn << 2] , ans[maxn << 2];        //sum[]存线段树,ans[]存结果
int vis[N] , a[maxn];    //vis[]第一次出现的位置,a[]存储叶子节点信息
struct Section {        //区间
    int L , R;
    int index;
    bool operator < (const Section tmp) const {
        return R < tmp.R;
    }
} s[maxn << 2];        
void PushUp(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void build()
{
    memset(sum , 0 , sizeof(sum));
    memset(vis , 0 , sizeof(vis));
}
void update(int pos , int x , int l , int r , int rt)
{
    if(l == r) {
        sum[rt] = x;
        return;
    }
    int m = (l + r) >> 1;
    if(pos > m)
        update(pos , x , rson);
    else
        update(pos , x , lson);
    PushUp(rt);
}
LL query(int L , int R , int l , int r , int rt)
{
    if(L <= l && R >= r) {
        return sum[rt];
    }
    int m = (l + r) >> 1;
    if(m < L)
        return query(L , R , rson);
    else if(m >= R)
        return query(L , R , lson);
    else
        return query(L , R , lson) + query(L , R , rson);
}
int main()
{
    int T , i , j , n , m;
    cin >> T;
    while(T--)
    {
        build();
        scanf("%d" , &n);
        for(i = 1 ; i <= n ; i++) {
            scanf("%d" , &a[i]);
            if(!vis[a[i]])        //a[i]第一次出现的位置
                vis[a[i]] = i;
            update(i , a[i] , 1 , n , 1);    //实际是在这里建的树
        }
        scanf("%d" , &m);
        for(i = 1 ; i <= m ; i++) {
            scanf("%d %d" , &s[i].L , &s[i].R);
            s[i].index = i;
        }
        sort(s + 1 , s + m + 1);
        for(i = 1 , s[0].R = 1; i <= m ; i++) {        //此为步骤③
            for(j = s[i - 1].R ; j <= s[i].R ; j++) {
                if(vis[a[j]] != j) {
                    int tmp = vis[a[j]];
                    update(tmp , 0 , 1 , n , 1);    //删掉之前出现的该数字
                    vis[a[j]] = j;                    //删掉后再换位置
                }
            }
            ans[s[i].index] = query(s[i].L , s[i].R , 1 , n , 1);
        }
        for(i = 1 ; i <= m ; i++)
            printf("%I64d
" , ans[i]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/H-Vking/p/4296296.html