hdu 3874 Necklace 线段树 单点更新

题意:求一个序列中所有数字的和,其中数值相同的只能计算一次。

做法:可以先储存所有的请求,然后按照它们的右边界排序,在查询的同时更新区间。这里其实有一点点DP的味道,在它进行某个查询之前,保证所有的重复数字都被删除光了,并且有不能影响其他查询,所以呢,只能从最近的那个操作进行计算。1次query即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#define left l,m,x<<1
#define right m+1,r,x<<1|1
#define LL __int64
const int LMT=50005;
using namespace std;
int a[LMT],pre[1000003];
LL ans[200003],sum[LMT<<2];
struct line
{
    int l,r,id;
    bool operator<(const line &y)const
    {
        return r<y.r;
    }
}e[200003];
void init(void)
{
    memset(pre,-1,sizeof(pre));
    memset(sum,0,sizeof(sum));
}
void update(int op,int pos,int l,int r,int x)
{
    if(l==r)
    {
        sum[x]=op;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)update(op,pos,left);
    if(pos>m)update(op,pos,right);
    sum[x]=sum[x<<1]+sum[x<<1|1];
}
LL query(int L,int R,int l,int r,int x)
{
    if(L<=l&&r<=R)
        return sum[x];
    LL res=0;
    int m=(l+r)>>1;
    if(L<=m)res+=query(L,R,left);
    if(R>m)res+=query(L,R,right);
        return res;
}
int main(void)
{
    int T,n,m,i,st;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&m);
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&e[i].l,&e[i].r);
            e[i].l--;e[i].r--;
            e[i].id=i;
        }
        sort(e,e+m);
        st=0;
        for(i=0;i<n&&st<m;i++)
        {
            if(pre[a[i]]!=-1)update(0,pre[a[i]],0,n-1,1);
            update(a[i],i,0,n-1,1);
            while(st<m&&e[st].r<=i)
            {
                if(e[st].r==i)
                    ans[e[st].id]=query(e[st].l,e[st].r,0,n-1,1);
                st++;
            }
            pre[a[i]]=i;
        }
        for(i=0;i<m;i++)printf("%I64d\n",ans[i]);
    }
    return 0;
}


 

 

你可能感兴趣的:(hdu 3874 Necklace 线段树 单点更新)