hdu 3333 离线+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3333
题意:

求一个区间内不重复数字的和,例如1 1 1 3,区间[1,4]的和为4。

分析:

我们考虑每个查询[l,r],现在要求的是这个区间里不通数的和,所以我们在从左扫到右的过程中,可以用一个数据结构保存已经得到的不同的数,到右端点后,然后对区间里不同的数求和,这样就可以得到答案了。
数据结构可以采用树状数组,那么我们是不是对每个查询区间都扫一遍呢?,显然是不行的,时间复杂度太大,我们只扫一遍即可!因为我们已经用树状数组保存了到i点之前所有的不同的数,当我们扫到某个查询区间的右端点时,我们只需要查询[l,r]这段区间即可。
当然查看是否扫到了某个右区间端点,可以先按右端点排序,或者直接用vector保存右端点相同的区间。

代码:

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <set>

using namespace std;
typedef long long ll;
const int N = 1e5 + 9;
int  a[N], n, q;
ll c[N], ans[N];

int lowbit (int x) {
    return x & (-x);
}
void update (int x, int val) {
    for (int i = x; i <= n; i += lowbit (i) )
        c[i] += val;
}
ll getsum (int x) {
    ll ans = 0;
    for (int i = x; i >= 1; i -= lowbit (i) )
        ans += c[i];
    return ans;
}
ll query (int l, int r) {
    return getsum (r) - getsum (l - 1);
}
struct node {
    int l, id;
};
vector<node>inv[N];
map<int, int>mmap;
int main() {
    //freopen("f.txt","r",stdin);
    int T;
    scanf ("%d", &T);
    while (T-- ) {
        scanf ("%d", &n);
        int l, r;
        for (int i = 1; i <= n; i++) scanf ("%d", &a[i]), inv[i].clear();
        scanf ("%d", &q);
        for (int i = 1; i <= q; i++) {
            scanf ("%d%d", &l, &r);
            inv[r].push_back ( (node) { l, i });
        }
        memset (c, 0, sizeof (c) );
        mmap.clear();
        for (int i = 1; i <= n; i++) {
            if (mmap.count (a[i]) ) update (mmap[a[i]], -a[i]);
            mmap[a[i]] = i;
            update (i, a[i]);

            for (int j = 0; j < inv[i].size(); j++)
                ans[inv[i][j].id] = query (inv[i][j].l, i);
        }
        for (int i = 1; i <= q; i++)
            printf ("%lld\n", ans[i]);
    }
    return 0;
}
/*

Sample Input

2
3
1 1 4
2
1 2
2 3
5
1 1 2 1 3
3
1 5
2 4
3 5

Sample Output

1
5
6
3
6

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值