HDU 3333 Turing Tree 离线处理+树状数组

 题目传送门:Problem - 3333

 

样例输入:

2
3
1 1 4
2
1 2
2 3
5
1 1 2 1 3
3
1 5
2 4
3 5

样例输出:

1
5
6
3
6

题目大意:

给你一段数字,q次询问,每次询问输出区间内不同数值的和,也就是说,区间内相同数只算一次。

解题思路:

一开始做这题的时候,刚学数据结构,就会几个板子,这题算是有难度的题了,放集训题单里,就是拿来防AK的,不过咱有万能的小伙伴,刷刷写出来了,也给我讲了思路,他是按询问的左边界排序,而我用的询问的右边界排序,也不懂什么是离线操作,这个大概就算吧。按照右边界排序之后,每次计算区间内最后一次出现的数,也就是没出现过的数,用更新树状数组,存储坐标,到下一次出现的时候,将树状数组对应位置归零,也就是加-a[i],再在新的位置更新,同时更新坐标,接着就是计算利用树状数组计算答案。

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std;
using LL=long long;
const int N=3e4+5;
const int M=1e5+5;
int n,q;
LL a[N];
LL tree[N];
int l[N],r[N];
map <LL,int> mp;
struct node{
    int l,r;
    int i;
}h[M];
LL ans[M];
bool cmp(struct node a,struct node b)
{
    if(a.r==b.r) return a.l<b.l;
    else return a.r<b.r;
}
int lowbit(int t)
{
    return t&-t;
}
void update(int idx,LL val)
{
    while(idx<=n)
    {
        tree[idx]+=val;
        idx+=lowbit(idx);
    }
}
LL query(int idx)
{
    LL res=0;
    while(idx)
    {
        res+=tree[idx];
        idx-=lowbit(idx);
    }
    return res;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for (int i=1; i<=n; i++) scanf("%lld",&a[i]);
        scanf("%d",&q);
        for (int i=1;i<=q; i++) {
            scanf("%d%d",&h[i].l,&h[i].r);
            h[i].i=i;
        }
        sort(h+1, h+q+1, cmp);
        memset(l, 0, sizeof(l));
        memset(r, 0, sizeof(r));
        memset(tree, 0, sizeof(tree));
        int tem=1;
        l[h[tem].r]=1;
        r[h[tem].r]=1;
        for (int i=2; i<=q; ) {
            while(h[i].r==h[tem].r&&i<=q) i++;
            l[h[tem].r]=tem;
            r[h[tem].r]=i-1;
            tem=i;
        }
        mp.clear();
        for (int i=1; i<=n; i++) {
            if(!mp[a[i]])
            {
                mp[a[i]]=i;
                update(i, a[i]);
            }else{
                update(mp[a[i]], -a[i]);
                update(i, a[i]);
                mp[a[i]]=i;
            }
            if(!r[i]) continue;
            int L=l[i],R=r[i];
            LL x=query(i);
            for (int i=L; i<=R; i++) {
                LL ans1=x;
                ans1-=query(h[i].l-1);
                ans[h[i].i]=ans1;
            }
        }
        for (int i=1; i<=q; i++) printf("%lld\n",ans[i]);
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值