zoukankan      html  css  js  c++  java
  • [ 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;
    }
    
  • 相关阅读:
    Centos设置开机启动Apache和Mysql[总结]
    主机无法访问虚拟机的apache解决办法
    PHP使用DOM XML操作XML[总结]
    Linux下C结构体初始化[总结]
    libxml2的安装及使用[总结]
    Linux动态链接库的使用
    linux获取系统启动时间
    解析oui.txt文件,通过MAC前缀获取Organization
    linux命令学习——cat
    linux内核数据结构之kfifo
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9609958.html
Copyright © 2011-2022 走看看