HDU 3874 Necklace

26 篇文章 0 订阅
24 篇文章 0 订阅

HDU 3874 Necklace

线段树,离线询问

题意

给一个数组,m个询问。每次询问一个区间,求区间内数的和,重复数只计算一次。

思路

离线询问后按右端点排序,从左往右扫数字,开个hash记录上次该数出现的位置。线段树模拟数组,出现就在位置上放数,没出现就置零,统计区间和。

这样每次扫到一个数,如果他没出现,就放到线段树上;如果他出现了,那么把它从上次出现的位置删掉,放到新的位置,更新hash数组。

每次询问统计区间和。

代码

1A就是美滋滋

#include<bits/stdc++.h>

#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long LL;
using namespace std;
const int MAXN=50007;
const int oo=0x3f3f3f3f;
LL stree[MAXN<<2];
void pushup(int rt)
{
    stree[rt]=stree[rt<<1]+stree[rt<<1|1];
}
void build(int l, int r, int rt)
{
    M(stree, 0);
}
void update(int pos, int c, int l, int r, int rt)
{
    if(l==r) { stree[rt]+=c;return; }
    int mid=(l+r)>>1;
    if(pos<=mid) update(pos, c, lson);
    else update(pos, c, rson);
    pushup(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
    if(L<=l&&r<=R) return stree[rt];
    int mid=(l+r)>>1;
    LL res=0;
    if(L<=mid) res+=query(L, R, lson);
    if(mid<R) res+=query(L, R, rson);
    return res;
}
int num[MAXN];
int ha[1000007];
struct Query
{
    int l, r;
    LL ans;
    int ne;
}q[MAXN<<2];
int head[MAXN];
int main()
{
    int T;scanf("%d", &T);
    while(T--)
    {
        int n;scanf("%d", &n);
        for(int i=1;i<=n;i++)
            scanf("%d", &num[i]);
        int m;scanf("%d", &m);
        build(1, n, 1);
        M(head, -1), M(ha, 0);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d", &q[i].l, &q[i].r);q[i].ans=0;
            q[i].ne=head[q[i].r];head[q[i].r]=i;
        }
        for(int i=1;i<=n;i++)
        {
            if(ha[num[i]]==0)
            {
                update(i, num[i], 1, n, 1);
                ha[num[i]]=i;
            }
            else
            {
                update(ha[num[i]], -num[i], 1, n, 1);
                update(i, num[i], 1, n, 1);
                ha[num[i]]=i;
            }
            for(int j=head[i];~j;j=q[j].ne)
            {
                int l=q[j].l, r=q[j].r;
                q[j].ans=query(l, r, 1, n, 1);
            }
        }
        for(int i=1;i<=m;i++)
        {
            printf("%lld\n", q[i].ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值