您的位置:首页 > 其它

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

2014-03-19 17:52 381 查看
HDU 3874 Necklace(树状数组+离线处理)
http://acm.hdu.edu.cn/showproblem.php?pid=3874


题意:
有一个由n个整数组成的数列,要求你回答以下询问[L,R]:返回a[L]到a[R]的和。但是其中相同值的元素只能计算一次。比如数列1 1 1 2 3 4 ,如果我们求[1,3]的和,就返回1。如果我们求[2,4]的和,就返回3。
分析:
预处理:首先我们需要一个树状数组A
,我们从左到右读入所有的a[i],如果当前a[i]=x值是第一次出现,就执行add(i,x),否则不执行。当我们扫描完了n个值后,我们保证sum(R)就是区间[1,R]的答案(想想是不是)。
接下来我们对所有查询的区间进行排序,使得[L,R]区间中L小的区间排在前面。我们首先用sum(R)给出所有区间[1,R]的查询。
接着我们要计算所有区间[2,R]的查询,但是做这步前,我们还需要消除a[1]对后面序列的影响,使得我们这个序列就好像是本来就是从a[1]开始的,从来没有存在过a[1]。要完成这个要求,我们只需要执行add(1,-a[1])并且我们需要找到a[1]值第二次出现的位置y执行add(y,a[1])。OK做完上面这步我们就保证了当前的sum(R)就是区间[2,R]的答案(有关区间[3,R],[4,R]等的查询我们目前不管,以后再说)。
所以每当我们查询完了区间[i-1,R]的答案后,我们需要做的操作是:
找到a[i-1]的值出现的下一个位置y(如果不存在为-1时,就默认为n+1位置),执行add(i-1,-a[i-1]) 和 add(y,a[i-1])即可。
我们用next[i]=j来表示与a[i]值相同的下一个值出现在j位置。用hash[h]=i表示值为h的a[i]第一次出现的位置是i。
答案可能超过int 需要用long long
AC代码:1640ms一次AC

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=50000+1000;
const int MAXV=1000000+100;
const int MAXM=200000+1000;
struct HASHMAP
{
    int head[MAXV];//head[x]=i表示第一个出现的x值下标是i
    int next[MAXN];
    int size;
    void init()
    {
        memset(head,-1,sizeof(head));
    }
    void insert(int i,int v)//将a[i]=v插入到HASHMAP中
    {
        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 find(int i)//找到a[i]值下一次出现的位置
    {
        return next[i];
    }
}hm;
long long c[MAXN];
int a[MAXN];//初始读入的值
struct node
{
    int l,r;
    int index;
    bool operator <(const node&b)const
    {
        return l<b.l;
    }
}nodes[MAXM];//查询命令
long long ans[MAXM];//最终答案
int lowbit(int x)
{
    return x&(-x);
}
long long sum(int x)
{
    long long res=0;
    while(x)
    {
        res +=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)
{
    while(x<MAXN)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d",&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)//目前不存在a[i]值
                add(i,a[i]);
            hm.insert(i,a[i]);
        }

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

        int j=1;//表示当前处理第j个排序后的区间
        for(int i=1;i<=n;i++)//从区间[i,R]一一处理
        {
            while(nodes[j].l==i)
            {
                ans[nodes[j].index]=sum(nodes[j].r);
                j++;
            }
            if(j>m)
                break;
            add(i,-a[i]);
            int next_ai=hm.find(i);
            if(next_ai!=-1)
                add(next_ai,a[i]);
        }
        for(int i=1;i<=m;i++)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: