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

想了一下午没有想到好办法,看了一下别人思路,该题需要离线处理。

这个方法可以很好的处理重复数字的问题,用来求解无重复数字的区间和问题。

将询问的区间按照右端点排序,每次记录上一个区间的右端点R,然后遍历R到当前区间右端点,用一个数组记录每一个数字出现的位置,如果该数字出现过,那么删除上一次出现的位置的值,并更新到当前位置。   这样就可以有效的去重 。   由于整个过程只是遍历了一遍数组,所以平摊意义下的时间复杂度很低。  

细节参见代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<list>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 50000 + 3;
const int maxm = 200000 + 5;
int T,n,m,l,r,b[maxn],vis[1000000+1];
ll bit[maxn+5],ans[maxm];
struct Edge{
    int l,r,id;
    bool operator < (const Edge& rhs) const {
        return r < rhs.r;
    }
}a[maxm];
ll sum(int x) {
    ll ret = 0;
    while(x > 0) {
        ret += bit[x]; x -= (x & -x);
    }
    return ret;
}
void add(int x,int d) {
    while(x <= maxn) {
        bit[x] += d; x += (x & -x);
    }
}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        memset(vis,0,sizeof(vis));
        memset(bit,0,sizeof(bit));
        for(int i=1;i<=n;i++) {
            scanf("%d",&b[i]);
            add(i,b[i]);
        }
        scanf("%d",&m);
        for(int i=0;i<m;i++)
            scanf("%d%d",&a[i].l,&a[i].r) , a[i].id = i;
        sort(a,a+m);
        int R = 0;
        for(int i=0;i<m;i++) {
            for(int j=R+1;j<=a[i].r;j++) {
                if(vis[b[j]]) {
                    add(vis[b[j]],-b[j]);
                    vis[b[j]] = j;
                }
                else vis[b[j]] = j;
            }
            R = a[i].r;
            ans[a[i].id] = sum(a[i].r)-sum(a[i].l-1);
        }
        for(int i=0;i<m;i++) printf("%I64d\n",ans[i]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值