hdu3333 Turing Tree(线段树/树状数组离线)

题意:

给一个长度为n的序列a,q组询问,每组询问给出L和R,问[L,R]中出现过的数字之和(出现过多次的仅计算一次)

数据范围:n<=5e4,a(i)<=1e9,q<=1e5

解法:
线段树很难直接维护,因为无法统计每一个区间节点中有哪几种数,很难快速将子节点的信息合并

用last[i]表示数a[i]上一次出现的位置
对于一组询问[l,r],如果last[i]<l,那么说明这个a[i][l,r]中第一次出现,对答案有贡献
如果last[i]>=l,说明这个a[i]已经在区间内出现过了,对答案无贡献,不计入答案
那么可以把问题转化为,对于每组询问,计算[l,r]中last[i]<l的a[i]的和
可以离线求解,对last和query排序,顺序枚举询问query,把满足last[i]<l的所有a[i]加入线段树的位置i
然后当前询问的答案就是线段树内[l,r]的和,因为线段树中[l,r]的数的last一定小于当前询问的l

代码用的树状数组,因为码量小
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
struct BIT{
    int c[maxm];
    int lowbit(int i){return i&-i;}
    void init(){memset(c,0,sizeof c);}
    void add(int i,int t){while(i<maxm)c[i]+=t,i+=lowbit(i);}
    int ask(int i){int ans=0;while(i)ans+=c[i],i-=lowbit(i);return ans;}
}bit;
struct Q{
    int l,r,id;
    bool operator<(Q a){
        return l<a.l;
    }
}query[maxm];
struct L{
    int l,id,v;
    bool operator<(L a){
        return l<a.l;
    }
}last[maxm];
int mark[maxm];
int ans[maxm];
int xx[maxm];
int a[maxm];
int n,q;
signed main(){
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i],xx[i]=a[i];
        sort(xx+1,xx+1+n);
        int num=unique(xx+1,xx+1+n)-xx-1;
        for(int i=1;i<=num;i++)mark[i]=0;
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(xx+1,xx+1+num,a[i])-xx;
            last[i]={mark[a[i]],i,xx[a[i]]};
            mark[a[i]]=i;
        }
        sort(last+1,last+1+n);
        //
        int q;cin>>q;
        for(int i=1;i<=q;i++){
            cin>>query[i].l>>query[i].r;
            query[i].id=i;
        }
        sort(query+1,query+1+q);
        //
        bit.init();
        int k=1;
        for(int i=1;i<=q;i++){
            int l=query[i].l,r=query[i].r;
            while(k<=n&&last[k].l<l){
                bit.add(last[k].id,last[k].v);
                k++;
            }
            ans[query[i].id]=bit.ask(r)-bit.ask(l-1);
        }
        for(int i=1;i<=q;i++)cout<<ans[i]<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值