HDU 3874 线段树+区间去重+妙啊

题目就不放了,占空间。

题目意思是给你一串序列,有m次询问,求区间不重复的值的和。

然后,需要用到很巧妙的处理方法。

记录一个值从左到右的出现的顺序,然后将他们串联起来,即右边一个记录前一个出现的位置,再右边一个记录这一个出现的位置,就和记录路径一样,然后我们就可以去重了。

我们对查询的区间扫描一次,每次遇到一个值,如果它之前还有一个相同的值,就把前一个相同的值删去,(用线段树的点更新实现,将前一个位置置为0)

但是,光是这样去重还是会超时的,还需要把询问都存下来,然后排一个序,这样后面的查询就可以用到之前的去重了。

 

由于是借鉴的别人的代码,先贴一份别人的代码,加点注释,等把代码忘了再自己敲,题目不错,不想浪费

 

#include <iostream>
#include <cstdio>
#include<map>
#include <cstring>
#include<vector>
#include <string>
#include<map>
#include<algorithm>
#include<cmath>
#include<set>
#define mem(a,val) memset(a,val,sizeof a)
#define lef rt<<1
#define rig rt<<1|1
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define mid (l+r)>>1
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
#define lson o<<1, l, m
#define rson o<<1|1, m+1, r
using namespace std;
typedef long long LL;
const int maxn = 50005;
const int MAX = 0x3f3f3f3f;
const int mod = 1000000007;
int t, n, m, A, B, in[maxn], last[maxn], pre[1000005];  //last记录每个点的前一个相同的点的位置,不存在记录为-1.
LL sum[maxn<<2], ans[200005];
struct C {
    int l, r, num;
}a[200005];
bool cmp (C x, C y) {
    return x.r < y.r;
}
 
void up(int o) {
    sum[o] = sum[o<<1] + sum[o<<1|1];
}
 
void build(int o, int l, int r) 
{
    if(l == r) sum[o] = in[l];
    else 
    {
        int m = (l+r) >> 1;
        build(lson);
        build(rson);
        up(o);
    }
}
 
void update(int o, int l, int r)
{
    if(l == r) 
    {
        sum[o] = 0;
        return ;
    }
    int m = (l+r) >> 1;
    if(A <= m) update(lson);
    else update(rson);
    up(o);
}
 
LL query(int o, int l, int r) 
{
    if(A <= l && r <= B) 
        return sum[o];
    int m = (l+r) >> 1;
    LL res = 0;
    if(A <= m) res += query(lson);
    if(m < B ) res += query(rson);
    return res;
}
 
void Ini() 
{
    memset(pre, -1, sizeof(pre));
    memset(last, -1, sizeof(last));
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) 
    {
        scanf("%d", &in[i]);
        if(pre[ in[i] ] != -1)  //如果已经出现过
        {
            last[i] = pre[ in[i] ]; //记录上一次出现的位置
            pre[ in[i] ] = i;   //记录这一次位置
        }
        else pre[ in[i] ] = i;
    }
}
 
int main()
{
    scanf("%d", &t); while (t--) 
    {
        Ini();
        scanf("%d", &m);
        for(int i = 1; i <= m; i++) 
        {
            scanf("%d%d", &a[i].l, &a[i].r);
            a[i].num = i;
        }
        sort(a+1, a+m+1, cmp);  //按照 右端点排序
        build(1, 1, n);
        int fr = 1;
        for(int i =1; i <= m; i++) 
        {
            for(int j = fr; j <= a[i].r; j++)
                if(last[j] != -1) 
                {
                    A = last[j];    
                    update(1, 1, n);    //点更新 ,值为0
                }
            A = a[i].l;
            B = a[i].r;
            ans[ a[i].num ] = query(1, 1, n);   //区间查询
            fr = a[i].r+1;
        }
        for(int i = 1; i <= m; i++) 
            printf("%I64d\n", ans[i]);
    }
    return 0;
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值