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

    题目描述

    HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

    输入格式

    第一行:一个整数N,表示项链的长度。

    第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

    第三行:一个整数M,表示HH 询问的个数。

    接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

    输出格式

    M 行,每行一个整数,依次表示询问对应的答案。


    做法1:线段树/树状数组:

    可以知道的是,我们对于每一种颜色都只需要其最近的一个。因为如果最近的一个都不在当前区间,那么之前的就都不会在当前区间了。

    那么设c(x)表示1~x包含多少种颜色。那么由于我们只需要知道当前颜色col最近的一个位置,当一个新的col色的贝壳时,我们要能够删掉之前的col的位置,并且加入当前位置。所以我们需要一个支持单点修改、区间查询的数据结构。可以用线段树或者树状数组。

    设vis(i)表示颜色i最近一次出现的位置。那么当新的颜色i的贝壳出现时:

    Delete(vis[x]),vis[x]=now,Add(now);
    

    所以我们可以先存下所有询问,然后按照右端点下标从小到大排序,然后枚举一个指针扫描过去,不断进行刚才更新颜色的操作,然后回答询问即可。

    时间复杂度为O(MlogM+(N+M)logN)。附上树状数组的代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define maxn 500001
    #define maxval 1000001
    using namespace std;
    int n,m,col[maxn],ans[maxn];
    inline int read(){
        register int x(0),f(1); register char c(getchar());
        while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
        while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    
    int c[maxn];
    inline int lowbit(const int &x){ return x&(-x); }
    inline void add(int x,const int &y){ for(;x<=n;x+=lowbit(x)) c[x]+=y; }
    inline int sum(int x){ register int ans=0; for(;x;x-=lowbit(x)) ans+=c[x]; return ans; }
    inline int ask(int l,int r){ return sum(r)-sum(l-1); }
    
    struct query{
        int l,r,id;
        bool operator<(const query &q)const{ return r<q.r; }
    }q[maxn];
    int vis[maxval];
    int main(){
        n=read();
        for(register int i=1;i<=n;i++) col[i]=read();
        m=read();
        for(register int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
    
        sort(q+1,q+1+m);
        int r=0;
        for(register int i=1;i<=m;i++){
            while(r+1<=q[i].r){
            	r++;
                if(vis[col[r]]) add(vis[col[r]],-1);
                add(r,1),vis[col[r]]=r;
            }
            ans[q[i].id]=ask(q[i].l,q[i].r);
        }
        for(register int i=1;i<=m;i++) printf("%d
    ",ans[i]);
        return 0;
    }
    

    做法2:莫队。

    莫队的板子题,设cnt(x)表示当前区间内颜色x出现的次数,每次往区间内加入一个点时就将这个点的颜色对应的cnt+1。如果cnt此时等于1,那么答案加1。从区间删点类似。

    时间复杂度为O(N√N+MlogM)

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define maxn 500001
    #define maxval 1000001
    #pragma GCC optimize(2)
    using namespace std;
    int n,m,unit,val[maxn];
    inline int read(){
        register int x(0),f(1); register char c(getchar());
        while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
        while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    void write(int x){
        if(!x) return;
        write(x/10),putchar(x%10+'0');
    }
    
    struct query{
        int l,r,id,col,ans;
    }q[maxn];
    int cnt[maxval],tot;
    bool cmp1(const query &a,const query &b){ return (a.col^b.col)?a.col<b.col:((a.col&1)?a.r<b.r:a.r>b.r); }
    inline bool cmp2(const query &x,const query &y){ return x.id<y.id; }
    inline void add(const int &x){ tot+=(++cnt[val[x]]==1); }
    inline void del(const int &x){ tot-=(--cnt[val[x]]==0); }
    inline void solve(){
        sort(q+1,q+1+m,cmp1);
        register int l=0,r=0;
        for(register int i=1;i<=m;i++){
            while(l<q[i].l) del(l++);
            while(l>q[i].l) add(--l);
            while(r<q[i].r) add(++r);
            while(r>q[i].r) del(r--);
            q[i].ans=tot;
        }
        sort(q+1,q+1+m,cmp2);
    }
    
    int main(){
        n=read(),unit=sqrt(n);
        for(register int i=1;i<=n;i++) val[i]=read();
        m=read();
        for(register int i=1;i<=m;i++){
            int l=read(),r=read();
            q[i].l=l,q[i].r=r,q[i].id=i,q[i].col=l/unit+1;
        }
        solve();
        for(register int i=1;i<=m;i++) write(q[i].ans),puts("");
        return 0;
    }
    
  • 相关阅读:
    vim使用
    .net程序员转java之mybatis学习
    Redis 学习笔记
    Effective C# 笔记01
    Android 开发感想
    入职两个月,WPF开发感想
    Oracle 学习笔记二
    Oracle 开发使用笔记一
    Java 集合——Map集合
    Java 集合——Set集合
  • 原文地址:https://www.cnblogs.com/akura/p/11066549.html
Copyright © 2011-2022 走看看