zoukankan      html  css  js  c++  java
  • 【BZOJ】[SDOI2009]HH的项链

    【算法】主席树||离线+树状数组

    【题解】

    主席树经典应用:找区间不同的数字个数。

    做法:记录每个数上一次出现的位置last[i],对last建权值线段树,对于区间询问last[i]<L的数字个数。

    注意权值范围是last[i],也即0~n。

    注意x=0时返回,否则可能c<0就完了。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    using namespace std;
    const int maxn=50010;
    struct tree{int l,r,sum;}t[1000010];
    int a[maxn],n,sz,last[maxn],root[maxn],b[1000010],m;
    
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    void insert(int l,int r,int &x,int y,int c){
        x=++sz;
        t[x]=t[y];t[x].sum++;
        if(l==r)return;
        int mid=(l+r)>>1;
        if(c<=mid)insert(l,mid,t[x].l,t[y].l,c);
        else insert(mid+1,r,t[x].r,t[y].r,c);
    }
    int ask(int l,int r,int x,int c){
        if(x==0)return 0;
        if(r<=c)return t[x].sum;
        int mid=(l+r)>>1,ans=0;
        if(c>mid)ans+=ask(mid+1,r,t[x].r,c);
        ans+=ask(l,mid,t[x].l,c);
        return ans;
    }    
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
            a[i]=read();
            last[i]=b[a[i]];
            b[a[i]]=i;
        }
        for(int i=1;i<=n;i++)insert(0,n,root[i],root[i-1],last[i]);
        m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read();
            printf("%d
    ",ask(0,n,root[v],u-1)-ask(0,n,root[u-1],u-1));
        }
        return 0;
    }
    View Code

    另一种做法,考虑对询问离线并按左端点排序,那么一个位置对答案有贡献仅当左端点已经过和它相同数字的上一位置,那么就是求区间和(有贡献的点+1),显然是树状数组。

    具体过程:初始同一个数字最前面的位置+1,然后1~n每次nex[i]位置+1,同时回答询问ans=ask(r)-ask(l-1),因为左端点经过了所以区间同一个数最多只存在一个。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #define lowbit(x) x&(-x)
    using namespace std;
    const int maxn=200010,maxL=1000010;
    int a[maxn],c[maxn],n,m,ans[maxn],anss[maxn],nex[maxn],b[maxL]; 
    
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    struct cyc{int l,r,id;}d[maxn];
    bool cmp(cyc a,cyc b){return a.l<b.l;}
    void insert(int x){for(int i=x;i<=n;i+=lowbit(i))c[i]++;}
    int ask(int x){int nowans=0;for(int i=x;i>=1;i-=lowbit(i))nowans+=c[i];return nowans;}
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            a[i]=read();
        }
        for(int i=0;i<=maxL;i++)b[i]=n+1;
        for(int i=n;i>=1;i--){
            nex[i]=b[a[i]];
            b[a[i]]=i;
        }
        for(int i=0;i<=maxL;i++)if(b[i])insert(b[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            d[i].l=read();d[i].r=read();
            d[i].id=i;
        }
        sort(d+1,d+m+1,cmp);
        int now=1;
        for(int i=1;i<=n&&now<=m;i++){
            while(now<=m&&d[now].l==i){ans[now]=ask(d[now].r)-ask(d[now].l-1);now++;}
            insert(nex[i]);
        }
        for(int i=1;i<=m;i++)anss[d[i].id]=ans[i];
        for(int i=1;i<=m;i++)printf("%d
    ",anss[i]);
        return 0;
    }
    View Code

    常数约为主席树的1/2。

  • 相关阅读:
    Redis:特殊数据类型,hyperloglog(基数),bitmap(位存储)
    Redis:特殊类型geospatial(地理位置类型,纬经度)
    Redis:zset常用指令
    Redis:hash常用指令
    Redis:set集合常用常用指令
    Pytorch学习-数据操作
    天池Python训练营笔记—Python基础入门:从变量到异常处理
    Python基础语法快速复习-面对对象编程
    Python基础语法快速复习-函数式编程
    Python基础语法快速复习-高级特性
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7471245.html
Copyright © 2011-2022 走看看