hdu3333 Turing Tree(离线处理+线段树)

题目:

给一个序列a,询问一个区间[l,r],输出区间内不同数字的和。
(1 ≤ N ≤ 30000,1 ≤ Q ≤ 100000)

分析:

这种题看出来是线段树做,但是线段树不能只加一个区间里不同的数字。从线段树入手是不可能了,所以想办法怎么能让一个区间内相同的数字只被计算一次。既然树不可更改,说明求和的时候相同的数只有一个在树里,想办法实现这个就可以了。

核心就是从小区间往大区间一点一点扩展,这棵树肯定不是一下子建成的,肯定是一点一点往里加点,边加边查的。不然不可能使得任意一个区间内相同数字都只算一个。我们将询问的区间全保存下来,然后按照右边界排序,先算右边界最小的,往大的慢慢扩展。
假如当前该算[l1,r1]这个区间了,那么我们只关注[1,r1],r1往右的部分我们还不考虑。[1,r1]内如何让每个数字只被计算1次呢?每个数字我们只记录最靠右的那个,左边的就当成0。然后直接普通的区间求和就可以了。假如下一个区间是[l2,r2],怎么更新这个树呢?我们可以发现需要添加进树的就是[r1+1,r2]这一段,所以先遍历这一段,看这一段的数字之前是否出现过,之前出现过就把前面的在树里变成0,这次的加进树。这样对于[1,r2]而言,每个数字依然只出现了一次,这时查[l2,r2]直接普通区间求和,肯定是对的。
以此类推。

代码:

#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN=3e4+5;
const int MAXM=1e5+5;
const double EPS=1e-8;
const int INF=0x3f3f3f3f;
const int MOD = 1e9+7;
int n,Q;
ll a[MAXN],ans[MAXM],tree[MAXN<<2];
map<ll,int> Hash;
struct Que{
    int l,r,id;
    bool operator < (const Que &a) const{
        return r < a.r;
    }
}ques[MAXM];
void pushup(int rt){
    tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
ll query(int L,int R,int rt,int l, int r){
    if(L<=l && R>=r){
        return tree[rt];
    }
    ll ret = 0;
    if(L <= (l+r)/2)    ret += query(L,R,lson);
    if(R > (l+r)/2) ret += query(L,R,rson);
    return ret;
}
void update(int rt,int l,int r,int pos,ll x){
    if(l==r){
        tree[rt] = x;
        return;
    }
    if(pos <= (l+r)/2)  update(lson,pos,x);
    else update(rson,pos,x);
    pushup(rt);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ms(ans,0);
        ms(tree,0);
        Hash.clear();
        for(int i=1;i<=n;i++)   scanf("%I64d",&a[i]);
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++){
            scanf("%d%d",&ques[i].l, &ques[i].r);
            ques[i].id = i;
        }
        sort(ques+1,ques+Q+1);
        int pos = 1;
        for(int i=1;i<=Q;i++){
            while(pos <= ques[i].r){
                if(Hash[a[pos]])
                    update(1,1,n,Hash[a[pos]],0);
                Hash[a[pos]] = pos;
                update(1,1,n,pos,a[pos]);
                pos++;
            }
            ans[ques[i].id] = query(ques[i].l,ques[i].r,1,1,n);
        }
        for(int i=1;i<=Q;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、付费专栏及课程。

余额充值