• 【Luogu】P1972HH的项链(链表+树状数组)


      题目链接

      难题,所以会讲得细一些。

      首先我们想如何统计区间[l,r]内不同贝壳的个数。

      第一个思路就是线段树/树状数组,query(1,r)-query(1,l-1)对不对?

      然而这样是不对的。

      然后我们举个例子:

      例如有一段区间是[ 1 2 3 1 2 3 1 2 3 ]这样子,如果要统计不同贝壳的个数,那么一个贝壳就可以代表所有同色贝壳。

      也就是说,假设要统计这个区间内1有没有出现,那这个区间变成这样子:[ 1 2 3 0 2 3 0 2 3 ] 或 [ 0 2 3 1 2 3 1 2 3 ] 或什么样子,都是一样的,只要1出现过一次,那就说明1出现过了。

      所以可以把所有询问按左端点排序,左端点相同的按照右端点排序,然后挨个统计:

      设next[ j ] 表示:x为j位置贝壳的颜色,next[j]表示的就是j后面第一个颜色为x的位置。如在我们举的例子中,next[1]=4,next[2]=5,next[5]=8。

      然后我们在刚开始初始化的时候,只有所有颜色第一次出现的位置作为该颜色的代表贝壳,也就是说只有这几个位置有1个不同的贝壳。

      然后在扫描询问数组的时候,把q[i-1].l到q[i].l之间的不同贝壳个数更新。具体方法是把next[当前位置]所指向的位置不同的贝壳变成1。

      这样就可以树状数组查询了。

      

    #include<cstdio>
    #include<cstdlib>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    
    inline long long read(){
        long long num=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)){
            if(ch=='-')    f=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            num=num*10+ch-'0';
            ch=getchar();
        }
        return num*f;
    }
    
    struct line{
        int l,r,id,ans;
        bool operator <(const line &a)const{
            if(l!=a.l)    return l<a.l;
            return r<a.r;
        }
    }q[1000100];
    bool cmp(line a,line b){    return a.id<b.id;    }
    int n;
    int tree[1000100];
    inline void add(int pos){
        while(pos<=n){
            tree[pos]++;
            pos+=pos&(-pos);
        }
    }
    inline int query(int pos){
        int ans=0;
        while(pos){
            ans+=tree[pos];
            pos-=pos&(-pos);
        }
        return ans;
    }
    
    int pre[1001010];
    int next[1001010];
    int vis[1001010];
    int que[1001010];
    
    int main(){
        n=read();
        for(int i=1;i<=n;++i){
            que[i]=read();
            next[pre[que[i]]]=i;
            if(!pre[que[i]]){
                add(i);
                vis[i]=1;
            }
            pre[que[i]]=i;
        }
        int m=read();
        for(int i=1;i<=m;++i)    q[i]=(line){read(),read(),i};
        std::sort(q+1,q+m+1);
        q[0].l=1;
        for(int i=1;i<=m;++i){
            if(q[i-1].l!=q[i].l)
                for(int j=q[i-1].l;j<q[i].l;++j)
                    if(next[j]&&!vis[next[j]]){
                        vis[next[j]]=1;
                        add(next[j]);
                    }
            q[i].ans=query(q[i].r)-query(q[i].l-1);
        }
        std::sort(q+1,q+m+1,cmp);
        for(int i=1;i<=m;++i)    printf("%d
    ",q[i].ans);
        return 0;
    }
  • 相关阅读:
    通过char与varchar的区别,学习可变长的字符类型
    laravel向视图传递变量
    MySQL数据库几种常用的索引类型使用介绍
    Java小知识点总结01
    好的代码习惯
    刻意练习
    算法
    经常复习
    kibana查询语法 使用教程
    工作思考
  • 原文地址:https://www.cnblogs.com/cellular-automaton/p/7661936.html
Copyright © 2020-2023  润新知