hdu 3874 Necklace 线段树单点更新区间求和

http://acm.hdu.edu.cn/showproblem.php?pid=3874

题意:

给你n个数,然后给出m个的询问[l,r]问[l,r]中不同的数的和为多少?

思路:
单纯只考虑不同数的和的话,可能该数在之前出现过,后边又出现了,但是所求的区间不包括前边那个数,那么这个数就要被加进来。关键是如何维护该区间不包含重复计算同一个数。

首先我们离线处理所有询问,然后按r排序,然后遍历一边处理那些还没被加进线段树的数,如果他之前出现过了,那么就将之前的删除,然后再吧后边的加进来,这样既能保证同一个数只被计算的一次了,而且不影响后边的询问。

这里关键是按r排序之后的处理:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val) memset(arr, val, sizeof(arr))

#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout)


#define M 200007
#define N 50007
#define ll __int64
using namespace std;
const double eps = 1e-10;

struct node
{
    int l,r;
    int id;
}nd[M];

ll val[4*N];
ll ans[M];
int a[N];
int off[1000007];

int n;

void pushup(int rt)
{
    val[rt] = val[rt<<1] + val[rt<<1|1];
}
void build(int l,int r,int rt)
{
    val[rt] = 0;
    if (l == r) return ;
    int m = (l + r)>>1;
    build(lc);
    build(rc);
}
void update(int pos,ll sc,int l,int r,int rt)
{
    if (l == r)
    {
        val[rt] += sc;
        return ;
    }
    int m = (l + r)>>1;
    if (pos <= m) update(pos,sc,lc);
    else update(pos,sc,rc);
    pushup(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
    if (l >= L && r <= R) return val[rt];
    ll res = 0;
    int m = (l + r)>>1;
    if (L <= m) res += query(L,R,lc);
    if (R > m) res += query(L,R,rc);
    return res;
}

int cmp(node a,node b) { return a.r < b.r; }
int cmp2(node a,node b) { return a.id < b.id; }

int main()
{
//    Read();
    int i;
    int T,n,m;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (i = 1; i <= n; ++i) scanf("%d",&a[i]);
        scanf("%d",&m);
        for (i = 0; i < m; ++i)
        {
            scanf("%d%d",&nd[i].l,&nd[i].r);
            nd[i].id = i;
        }

        sort(nd,nd + m,cmp);
        build(1,n,1);
        CL(off,0);

        int s = 1;
        for (i = 0; i < m; ++i)
        {
            while (s <= nd[i].r)
            {
                if (off[a[s]]) update(off[a[s]],-a[s],1,n,1);

                update(s,a[s],1,n,1);
                off[a[s]] = s;
                s++;
            }
            ans[nd[i].id] = query(nd[i].l,nd[i].r,1,n,1);
        }

        for (i = 0; i < m; ++i)
        {
            printf("%I64d\n",ans[i]);
        }
    }
    return 0;
}
View Code

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值