• LuoguP1972书架


    其实我对树状数组及一系列据结构掌握一直如同菜鸟

    打了这么久连别人认为是模板的题都想不出来

    所以我采用题海战术,妄想有些突破

    题目链接


    本题的意思是选取随机一段l~r的区间,询问这个区间里有多少种不同贝壳。

    本来看到区间统计两个词语就应该想到树状数组&线段树

    但是仔细一想,如果是线段树的合并,必须满足相加的性质(例如数量,长度等)或者一些可以“拼接的东西”(例如hash值,相同颜色小球的数量等……)这些便可以用线段树的合并。

    而现在是统计种类的多少,显然不能用普通的合并(1,3,5)&(1,4,5) 你总不能说合并后种类为3+3==6种吧(显然是4种)

    重点来了

    重点来了

    终点来了

    那我们怎么统计种类的多少呢?

    仔细分析一下题目性质,要求我们求l~r之间种类数量,如果我们普通的模板解决不了怎么办?

    我们假设我们求的是1~l,我们会怎么求

    这个时候思路应该就会比较明显,但是还是没有到想出来的地步(所以没想出来的朋友们不要慌)

    我们直接for循环过去,用一个vis数组保存,如果碰到一个数x,!vis[x],那么我们的ans++;(ans代表的是颜色种类)

    如果vis[x]>0 ,那我们就直接continue; 对不对?

    那么代码如下:

    (```)

    for(register int i=l;i<=r;i++){
        if(!vis[color[i]]) vis[color[i]]=true,ans++;
        //color[i]代表的是i号数字的种类
    }
    

    (```)

    这个时候时间复杂度就是n*m(长度乘以询问数)

    想不到更好的算法怎么办?

    那就优化噻!

    我们依据我们算法的思想——本质上是统计出现的种类,重复的舍去,而我们要求的lr又自然地想到1l 和 1~r两个区间,那么我们冥思苦想,怎么才能用区间表示呢

    我们将集合S代表1~l-1 & lr两个区间公有的元素,那么我们可以只选择1l-1的元素++,或者只选择lr的元素++;我们要统计lr,那么我们理所应当加在l~r上:

    将其通俗一点,便是在循环遍历时,if(vis[color[i]]) 上一个color[i]的位置--;i++;

    这个时候便可以用树状数组统计。

    而区间的话我们可以离线处理,因为1l这个初始点并不能包含这个lr,所以说我们按照r的大小排序,这样上一个color[i]--时,就不会影响答案

    所以完整的代码如下

    (```)

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define Starseven main
    #define ri register int
    using namespace std;
    const int N=1e6+20;
    int n,m,cnt[N],before[N],tr[N];
    
    struct node{
        int kind,loc;
    }num[N];
    
    struct noe{
        int ans,l,r,id;
    }area[N];
    
    bool cmp(const node &a,const node &b){
        if(a.kind!=b.kind) return a.kind<b.kind;
        else return a.loc<b.loc;
    }
    
    bool fuck(const noe &a,const noe &b){
        return a.r<b.r;
    }
    
    void Insert(int x,int va){
        for(ri i=x;i<=n;i+=i&-i) tr[i]+=va;
    }
    
    int Getsum(int x){
        int re=0;
        for(ri i=x;i;i-=i&-i) re+=tr[i];
        return re;
    }
    
    bool again(const noe &a,const noe &b){
        return a.id<b.id;
    }
    
    int read();
    void write(int); 
    void wrote(int);
    int Starseven(){
        n=read();
        for(ri i=1;i<=n;i++){
        	num[i].loc=i;cnt[i]=num[i].kind=read();	
        }
        sort(num+1,num+1+n,cmp);
        for(ri i=1;i<=n;i++){
        	before[num[i].loc]=num[i].kind==num[i-1].kind?num[i-1].loc:0;
        }
        m=read();
        for(ri i=1;i<=m;i++){
    	area[i].l=read(),area[i].r=read();
    	area[i].id=i;	
        }
        sort(area+1,area+1+m,fuck);
        int tail=1;
        for(ri i=1;i<=n;i++){
    	Insert(i,1);
    	if(before[i]) Insert(before[i],-1);
    	while(area[tail].r==i){
    		area[tail].ans=Getsum(i)-Getsum(area[tail].l-1);
    		tail++; 
    	}
        }
        sort(area+1,area+1+m,again);
        for(ri i=1;i<=m;i++) write(area[i].ans);
        return 0;
    } 
    
    int read(){
    char ch=getchar();int re=0,op=1;
    while(ch<'0'||ch>'9'){
    	if(ch=='-')op=-1;
    	ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
    	re=(re<<3)+(re<<1)+ch-'0';
    	ch=getchar();
    }
    return re*op;
    }
    
    inline void write(int x){
    wrote(x);
    puts("");
    }
    
    inline void wrote(int x)
    {
        if(x<0){
    	    putchar('-');
        	x=-x;
        }
        if(x>9) 
    	wrote(x/10);
        putchar(x%10+'0');
    }
    

    (```)

  • 相关阅读:
    Object C学习笔记25-文件管理(一)
    实施项目--为什么开发人员一直在抱怨需求变动
    Git.Framework 框架随手记--准备工作
    一网打尽!2018网络安全事件最全的盘点
    林纳斯·托瓦兹和Linux行为准则:揭穿7个谬论
    LinkedList源码解析
    四种List实现类的对比总结
    HashMap源码解析
    volatile
    Java内存模型与共享变量可见性
  • 原文地址:https://www.cnblogs.com/starseven/p/12463353.html
Copyright © 2020-2023  润新知