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

来源:互联网 发布:java官方下载地址 编辑:程序博客网 时间:2024/06/08 05:10

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

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

将询问的区间按照右端点排序,每次记录上一个区间的右端点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;}

0 0