HDU 3874 Necklace(树状数组+离线hash)

题意:

            一个由n个整数组成的数列,求区间[L,R]上的和,其中相同的元素只能算一次。

 

题解:

            对于原数组,hash记录每个元素出现的位置。然后把所有元素只添加一次进树状数组。离线初始数组和要查询的所有区间。把所有的区间按照L从小到大排序。

对于第一个区间,假设L=1,那么直接就sum(R)就是第一个区间的答案了,对于以后的区间如果L还是为1的话,继续sum(R)就是了。一直把所有的L=1的区间全部求出来.

然后去求L=2的区间,但是要把L=1的情况减去,只需要add(1,-a[1])即可,并且要把下次出现a[1]x位置(hash记录了),执行add(x,a[1])相当于前面L=1没出现过,L=2是起始位置了。依次这样走下去求出所有的答案。

如果区间中L并不存在为1,2等等的情况,直接减去,找到下次出现a[i]的位置,添加到树状数组即可。然后去找下一个L存在的即可。


#include<bits/stdc++.h>

using namespace std;

const int maxn = 50011;
const int maxm = 200000+100;
const int maxv = 1000000+100;
long long c[maxn];
int a[maxn];
long long ans[maxm];
struct node
{
    int l,r;
    int index;
    bool operator < (const node& a)const
    {
        return l<a.l;
    }
}ns[maxm];


struct hashmap
{
    int head[maxv],next[maxn];
    void init()
    {
        memset(head,-1,sizeof head);
    }

    void inser(int i,int v)
    {
        next[i] = -1;
        if(head[v]==-1)
            head[v]=i;
        else
        {
            int j = head[v];
            while(next[j]!=-1)
            {
                j = next[j];
            }
            next[j] = i;
        }
    }
    int fin(int i)
    {
        return next[i];
    }
}hm;

long long sum(int x)
{
    long long res=0;
    while(x>0)
    {
        res+=c[x];
        x-=(x&-x);
    }
    return res;
}

void add(int x,int v)
{
    while(x<maxn)
    {
        c[x]+=v;
        x+=(x&-x);
    }
}

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n;
        hm.init();
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            if(hm.head[a[i]]==-1)
                add(i,a[i]);
            hm.inser(i,a[i]);
        }

        cin>>m;

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&ns[i].l,&ns[i].r);
            ns[i].index=i;
        }
        sort(ns+1,ns+1+m);

        int j=1;
        for(int i=1;i<=n;i++)   
        {
            while(ns[j].l==i)
            {
                ans[ns[j].index]=sum(ns[j].r);///找完所有的区间为L的区间
                j++;
            }
            if(j>m)
                break;///所有的区间找完了
            add(i,-a[i]);///原来的位置减去a[i]

            if(hm.fin(i)!=-1)///在新的位置加上a[i]
            {
                add(hm.fin(i),a[i]);
            }
        }
        for(int i=1;i<=m;i++)
        {
            printf("%lld\n",ans[i]);
        }
    }

}

猜你喜欢

转载自blog.csdn.net/sgh666666/article/details/80447866