• [ SDOI 2009 ] HH的项链 & [ HEOI 2012 ] 采花


    (\)

    (Description)


    给出一个长为(N)的序列,(M)次询问区间([L_i,R_i])内不同数字的个数。

    • (Nin [1,5 imes 10^4])(Min [1,2 imes 10^5])(L_i,R_iin [1,N])

    (\)

    (Solution)


    • 离线做法,将询问按右端点从小到大排序。
    • 对下标开树状数组,每次添加数字在该位置(+1),在上一次该数字出现的位置(-1),然后将能回答询问以前缀和相减的方式都回答了。
    • 关于正确性,如上做法可以看成只为每个颜色保留最后一次出现的标记,因为将询问排序过,所以更新的树状数组所求出的答案一定是对于当前右端点是合法的。

    (\)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define R register
    #define N 500010
    #define M 200010
    using namespace std;
     
    int n,m,clr[N],ans[M],last[1000010];
     
    struct tasks{int l,r,num,ans;}tsk[M];
     
    struct BIT{
        int c[N];
        BIT(){memset(c,0,sizeof(c));}
        inline int lowbit(int x){return x&(-x);}
        inline void add(int x,int k){
            for(R int i=x;i<=n;i+=lowbit(i)) c[i]+=k;
        } 
        inline int sum(int x){
            int res=0;
            for(R int i=x;i;i-=lowbit(i)) res+=c[i];
            return res;
        }
    }bit;
     
    inline int rd(){
        int x=0;
        char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)){
            x=(x<<1)+(x<<3)+(c^48);
            c=getchar();
        }
        return x;
    }
     
    inline bool cmp(tasks a,tasks b){
        return (a.r==b.r)?(a.l<b.l):(a.r<b.r);
    }
     
    int main(){
        n=rd();
        for(R int i=1;i<=n;++i) clr[i]=rd();
        m=rd();
        for(R int i=1;i<=m;++i) {
            tsk[i].l=rd();
            tsk[i].r=rd();
            tsk[i].num=i;
        }
        sort(tsk+1,tsk+1+m,cmp);
        int now=1;
        for(R int i=1;i<=n;++i){
            bit.add(i,1);
            if(last[clr[i]]) bit.add(last[clr[i]],-1);
            last[clr[i]]=i;
            while(i==tsk[now].r){
                tsk[now].ans=bit.sum(i)-bit.sum(tsk[now].l-1);
                ++now;
            }
            if(now>m)break;
        }
        for(R int i=1;i<=m;++i) ans[tsk[i].num]=tsk[i].ans;
        for(R int i=1;i<=m;++i) printf("%d
    ",ans[i]);
        return 0;
    }
    

    (\)

    (Extend)


    将询问改为,求区间内至少出现过两次的不同数字个数。

    (\)

    • 注意到只有询问区间内出现两次数字才是有效的,但仿照之前的写法如果直接标记第二次出现的数,并取消第一次出现的数却会出错,是因为可能所谓的第一次出现的数并不在询问区间里,而所谓的第二个数却被算进了答案。
    • 于是做法就改为记录两次上一个出现的位置,每次出现一个数将两次前出现该数的位置(-1),一次前出现的位置(+1)即可,这样能保证一个数只算了一次,并且出现两次的数一定打过标记。

    (\)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 2000010
    #define R register
    #define gc getchar
    using namespace std;
     
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
     
    int n,c,m,s[N],lst1[N],lst2[N],ans[N];
     
    struct query{int l,r,num;}q[N];
     
    inline bool cmp(query x,query y){
      return x.r==y.r?x.l<y.l:x.r<y.r;
    }
     
    struct BIT{
      int c[N];
      BIT(){memset(c,0,sizeof(c));}
      inline int lowbit(int x){return x&-x;}
      inline void add(int p,int x){
        for(;p<=n;p+=lowbit(p)) c[p]+=x;
      }
      inline int sum(int p){
        int res=0;
        for(;p;p-=lowbit(p)) res+=c[p];
        return res;
      }
    }bit;
     
    int main(){
      n=rd(); c=rd(); m=rd();
      for(R int i=1;i<=n;++i) s[i]=rd();
      for(R int i=1;i<=m;++i){
        q[i].l=rd(); q[i].r=rd(); q[i].num=i;
      }
      sort(q+1,q+1+m,cmp);
      for(R int i=1,top=1;i<=n;++i){
        if(lst2[s[i]]) bit.add(lst2[s[i]],-1);
        if(lst1[s[i]]){bit.add(lst1[s[i]],1);}
        lst2[s[i]]=lst1[s[i]]; lst1[s[i]]=i;
        while(q[top].r==i){
          ans[q[top].num]=bit.sum(i)-bit.sum(q[top].l-1);
          ++top;
        }
      }
      for(R int i=1;i<=m;++i) printf("%d
    ",ans[i]);
      return 0;
    }
    
  • 相关阅读:
    解析大型.NET ERP系统 权限模块设计与实现
    Enterprise Solution 开源项目资源汇总 Visual Studio Online 源代码托管 企业管理软件开发框架
    解析大型.NET ERP系统 单据编码功能实现
    解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计
    Windows 10 部署Enterprise Solution 5.5
    解析大型.NET ERP系统 设计异常处理模块
    解析大型.NET ERP系统 业务逻辑设计与实现
    解析大型.NET ERP系统 多国语言实现
    Enterprise Solution 管理软件开发框架流程实战
    解析大型.NET ERP系统 数据审计功能
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9609958.html
Copyright © 2020-2023  润新知